SaleRequest Class

Stores information about a sales request.

Namespace

CommercePayments

Usage

This class holds all the required details about a sale request. Gateway adapters read the fields of this class object while constructing a sale JSON request thatis sent to the payment gateway. The object of this class is made available through commercepayments.paymentGatewayContext by calling getPaymentRequest().

Example

1/**
2     * Responsibilities:
3     * - Set merchant and reference details
4     * - Convert amount into minor units (e.g., cents)
5     * - Map payment method types (card, ACH, SEPA, etc.)
6     * - Include stored payment method token for recurring payments
7     * - Optionally include enhanced Level 2/3 data
8     *
9     * @param saleRequest Input request containing payment and shopper details
10     * @return JSON string payload for the payment gateway
11     */
12    private String buildSaleRequest(commercepayments.SaleRequest saleRequest) {
13
14        // Resolve currency (fallback to user's default if missing)
15        String currencyIso = saleRequest.currencyIsoCode;
16        if (currencyIso == null) {
17            currencyIso = UserInfo.getDefaultCurrency();
18        }
19
20        // Extract payment method
21        commercepayments.SaleApiPaymentMethodRequest paymentMethod = saleRequest.paymentMethod;
22
23        // Initialize JSON generator
24        JSONGenerator jsonGeneratorInstance = JSON.createGenerator(true);
25        jsonGeneratorInstance.writeStartObject();
26
27        // Merchant configuration (from Named Credential)
28        jsonGeneratorInstance.writeStringField('merchantAccount', '{!$Credential.Username}');
29
30        // Unique reference using timestamp + random suffix
31        jsonGeneratorInstance.writeStringField(
32            'reference',
33            String.valueOf(Datetime.now().getTime()) +
34            String.valueOf(Math.random()).substring(2, 8)
35        );
36
37        // Amount block
38        jsonGeneratorInstance.writeFieldName('amount');
39        jsonGeneratorInstance.writeStartObject();
40        jsonGeneratorInstance.writeStringField(
41            'value',
42            String.valueOf((saleRequest.amount * 100.0).intValue()) // convert to minor units
43        );
44        jsonGeneratorInstance.writeStringField('currency', currencyIso);
45        jsonGeneratorInstance.writeEndObject();
46
47        // Payment method block
48        jsonGeneratorInstance.writeFieldName('paymentMethod');
49        jsonGeneratorInstance.writeStartObject();
50
51        String shopperReference;
52        String type = 'scheme'; // default = card
53
54        // Handle stored payment method data (tokenized payments)
55        if (saleRequest.paymentMethodData != null) {
56
57            String token = saleRequest.paymentMethodData.get('gatewayToken');
58            String paymentMethodType = saleRequest.paymentMethodData.get('paymentMethodType');
59            shopperReference = saleRequest.paymentMethodData.get('gatewayReference');
60
61            // Map payment method types to gateway-specific values
62            if ('us_bank_account'.equals(paymentMethodType)) {
63                type = 'ach';
64            } else if ('sepa_debit'.equals(paymentMethodType)) {
65                type = 'sepadirectdebit';
66            } else if ('au_becs_debit'.equals(paymentMethodType)) {
67                type = 'directdebit_AU';
68            } else if ('bacs_debit'.equals(paymentMethodType)) {
69                type = 'directdebit_GB';
70            }
71
72            jsonGeneratorInstance.writeStringField('type', type);
73            jsonGeneratorInstance.writeStringField('storedPaymentMethodId', token);
74        }
75
76        // Add enhanced scheme data ONLY for card payments
77        // Note: Gateway might have validations on L2/L3 data so do test them out before using L2/L3 else transactions might fail
78        if (enhancedPaymentData != null && 'scheme'.equals(type)) {
79            jsonGeneratorInstance.writeFieldName('additionalData');
80            jsonGeneratorInstance.writeStartObject();
81            populateEnhancedSchemeData(jsonGeneratorInstance, enhancedPaymentData);
82            jsonGeneratorInstance.writeEndObject(); // additionalData
83        }
84
85        jsonGeneratorInstance.writeEndObject(); // paymentMethod
86
87        // Recurring / shopper configuration
88        jsonGeneratorInstance.writeStringField('shopperInteraction', 'ContAuth');
89        jsonGeneratorInstance.writeStringField('recurringProcessingModel', 'UnscheduledCardOnFile');
90        jsonGeneratorInstance.writeStringField('shopperReference', shopperReference);
91
92        // Immediate capture
93        jsonGeneratorInstance.writeNumberField('captureDelayHours', 0);
94
95        jsonGeneratorInstance.writeEndObject(); // root
96
97        return jsonGeneratorInstance.getAsString();
98    }
99
100    /**
101     * Populates Level 2 and Level 3 enhanced scheme data.
102     *
103     * @param jsonGeneratorInstance JSON generator
104     * @param enhancedPaymentData Enhanced payment data input
105     */
106    private void populateEnhancedSchemeData(JSONGenerator jsonGeneratorInstance,
107                                            commercepayments.EnhancedPaymentDataInput enhancedPaymentData) {
108
109        // -------- Level 2 fields --------
110
111        if (enhancedPaymentData.totalTaxAmount != null) {
112            jsonGeneratorInstance.writeStringField(
113                'enhancedSchemeData.totalTaxAmount',
114                toMinorUnits(enhancedPaymentData.totalTaxAmount)
115            );
116        }
117
118        if (enhancedPaymentData.shippingAmount != null) {
119            jsonGeneratorInstance.writeStringField(
120                'enhancedSchemeData.freightAmount',
121                toMinorUnits(enhancedPaymentData.shippingAmount)
122            );
123        }
124
125        if (enhancedPaymentData.discountAmount != null) {
126            jsonGeneratorInstance.writeStringField(
127                'enhancedSchemeData.discountAmount',
128                toMinorUnits(enhancedPaymentData.discountAmount)
129            );
130        }
131
132        if (enhancedPaymentData.invoiceNumber != null) {
133            jsonGeneratorInstance.writeStringField(
134                'enhancedSchemeData.customerReference',
135                enhancedPaymentData.invoiceNumber
136            );
137        }
138
139        // -------- Level 3 fields (line items) --------
140
141        if (enhancedPaymentData.lineItems != null) {
142            Integer index = 1;
143
144            for (commercepayments.LineItemInput item : enhancedPaymentData.lineItems) {
145                populateLineItemData(
146                    jsonGeneratorInstance,
147                    item,
148                    'enhancedSchemeData.itemDetailLine' + index + '.'
149                );
150                index++;
151            }
152        }
153
154        // Shipping / destination info
155        if (enhancedPaymentData.shipFromZip != null) {
156            jsonGeneratorInstance.writeStringField(
157                'enhancedSchemeData.shipFromPostalCode',
158                enhancedPaymentData.shipFromZip
159            );
160        }
161
162        if (enhancedPaymentData.shipToZip != null) {
163            jsonGeneratorInstance.writeStringField(
164                'enhancedSchemeData.destinationPostalCode',
165                enhancedPaymentData.shipToZip
166            );
167        }
168
169        if (enhancedPaymentData.shipToCountry != null) {
170            jsonGeneratorInstance.writeStringField(
171                'enhancedSchemeData.destinationCountryCode',
172                enhancedPaymentData.shipToCountry
173            );
174        }
175    }
176
177    /**
178     * Populates Level 3 line item data.
179     *
180     * @param jsonGeneratorInstance JSON generator
181     * @param item Line item input
182     * @param prefix Field prefix for indexed items
183     */
184    private void populateLineItemData(JSONGenerator jsonGeneratorInstance,
185                                      commercepayments.LineItemInput item,
186                                      String prefix) {
187
188        if (item.sku != null) {
189            jsonGeneratorInstance.writeStringField(prefix + 'productCode', item.sku);
190        }
191
192        if (item.name != null) {
193            jsonGeneratorInstance.writeStringField(prefix + 'description', item.name);
194        }
195
196        if (item.quantity != null) {
197            jsonGeneratorInstance.writeStringField(prefix + 'quantity', String.valueOf(item.quantity));
198        }
199
200        // Unit price is always written
201        jsonGeneratorInstance.writeStringField(
202            prefix + 'unitPrice',
203            toMinorUnits(item.unitPrice)
204        );
205
206        if (item.taxAmount != null) {
207            jsonGeneratorInstance.writeStringField(
208                prefix + 'taxAmount',
209                toMinorUnits(item.taxAmount)
210            );
211        }
212
213        if (item.discount != null) {
214            jsonGeneratorInstance.writeStringField(
215                prefix + 'discountAmount',
216                toMinorUnits(item.discount)
217            );
218        }
219
220        if (item.commodityCode != null) {
221            jsonGeneratorInstance.writeStringField(prefix + 'commodityCode', item.commodityCode);
222        }
223
224        if (item.uom != null) {
225            jsonGeneratorInstance.writeStringField(prefix + 'unitOfMeasure', item.uom);
226        }
227    }
228
229    /**
230     * Converts amount to minor units (e.g., dollars → cents).
231     *
232     * @param amount Decimal amount
233     * @return String representation of minor units
234     */
235    private static String toMinorUnits(Decimal amount) {
236        if (amount == null) return null;
237
238        Decimal value = (amount * 100)
239            .setScale(0, System.RoundingMode.HALF_UP);
240
241        return String.valueOf(value.intValue());
242    }

SaleRequest Constructors

The following are constructors for SaleRequest.

SaleRequest(amount)

Constructor for defining an amount for the sale request. This constructor is intended for test usage and throws an exception if used outside of the Apex test context.

Signature

global SaleRequest(Double amount)

Parameters

amount
Type: Double
Amount of the sale request.

SaleRequest Properties

The following are properties for SaleRequest.

accountId

Customer account ID for the sale request.

Signature

global String accountId {get; set;}

Property Value

Type: String

amount

Amount of the sale request. Can be positive only.

Signature

global Double amount {get; set;}

Property Value

Type: Double

comments

Additional information about the sale request.

Signature

global String comments {get; set;}

Property Value

Type: String

currencyIsoCode

Currency code for the sale request.

Signature

global String currencyIsoCode {get; set;}

Property Value

Type: String

enhancedPaymentData

Represents enhanced payment data, including Level 2 and Level 3 fields.

Supported only for third-party payment gateways; not supported for native payments.

Signature

public commercepayments.EnhancedPaymentDataInput enhancedPaymentData {get; set;}

Property Value

Type: commercepayments.EnhancedPaymentDataInput

paymentInitiationSourceId

ID of the source that initiated the payment.

Supported only for third-party payment gateways; not supported for native payments. See PaymentInitiationSource object documentation for more information.

Signature

public String paymentInitiationSourceId {get; set;}

Property Value

Type: String

paymentMethod

Payment method used in the sale request.

Signature

global commercepayments.SaleApiPaymentMethodRequest paymentMethod {get; set;}

Property Value

Type: SaleApiPaymentMethodRequest

paymentMethodData

Payment method data used in the sale request.

This field is populated when SaleInput specifies a saved payment method. Accessible using paymentMethodData on SaleRequest. The map contains these fields from SavedPaymentMethod: GatewayToken, Type, GatewayReference, and StandardEntryCode for direct gateway interaction without querying the database.

Signature

public Map<String,String> paymentMethodData {get; set;}

Property Value

Type: Map<String,String>

submittedByMerchant

Indicates whether the sale request is submitted by the marchant (true) or not (false).

Signature

public Boolean submittedByMerchant {get; set;}

Property Value

Type: Boolean

SaleRequest Methods

The following are methods for SaleRequest.

equals(obj)

Compares this object with the specified object and returns true if both objects are equal; otherwise, returns false.

Signature

global Boolean equals(Object obj)

Parameters

obj
Type: Object

Return Value

Type: Boolean

hashCode()

Maintains the integrity of lists of type SaleRequest by determining the uniqueness of the external object records in a list.

Signature

global Integer hashCode()

Return Value

Type: Integer

toString()

Converts a date to a string.

Signature

global String toString()

Return Value

Type: String