SaleRequest Class
Namespace
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)
Signature
global SaleRequest(Double amount)
Parameters
- amount
- Type: Double
- Amount of the sale request.
SaleRequest Properties
The following are properties for SaleRequest.
enhancedPaymentData
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
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
Signature
global commercepayments.SaleApiPaymentMethodRequest paymentMethod {get; set;}
Property Value
paymentMethodData
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;}
submittedByMerchant
Signature
public Boolean submittedByMerchant {get; set;}
Property Value
Type: Boolean
SaleRequest Methods
The following are methods for SaleRequest.
equals(obj)
Signature
global Boolean equals(Object obj)
Parameters
- obj
- Type: Object
Return Value
Type: Boolean
hashCode()
Signature
global Integer hashCode()
Return Value
Type: Integer