1public commercepayments.GatewayResponse createSaleResponse(commercepayments.SaleRequest saleRequest) {
2 // API: https://stripe.com/docs/api/payment_intents
3 String token;
4 String paymentMethodType;
5 Map < String, String > params = new Map < String, String > ();
6 Id idToProccess = saleRequest.paymentMethod.id;
7 // payment method is card if payment method data is not present or paymentMethodType is card
8 if (saleRequest.paymentMethodData == null) {
9 // resolved card token,
10 //Before v65.0 for card details, you need to query DB for respective card payment method record or if you are sending CPM id in saleRequest
11 system.debug('Payment method data is null');
12 String paymentMethodId = saleRequest.paymentMethod.id;
13 if (idToProccess.getSObjectType() == CardPaymentMethod.SObjectType) {
14 token = resolveCardToken(paymentMethodId);
15 } else if (idToProccess.getSObjectType() == SavedPaymentMethod.SObjectType) {
16 token = resolveSPMToken(paymentMethodId);
17 }
18 params.put('payment_method', urlEncode(token));
19 params.put('payment_method_types[]', 'card');
20 } else {
21 //NOTE: From v65.0 onwards, paymentMethodData would have all the details like token, type etc required fields for all payment methods(card, bank payment methods).
22 //paymentMethodData would have data in case of ACH
23 system.debug('Payment method data is not null');
24 if (saleRequest.amount == 999999 && idToProccess.getSObjectType() == SavedPaymentMethod.SObjectType) {
25 // NOTE: This is only for QA purposes. In an ideal scenario, querying the database record should be avoided unless absolutely necessary.
26 String paymentMethodId = saleRequest.paymentMethod.id;
27 token = resolveSPMToken(paymentMethodId);
28 system.debug('SPM query done when paymentMethodData was available');
29 } else {
30 token = saleRequest.paymentMethodData.get('gatewayToken');
31 }
32 System.debug('Value of gatewayToken ' + ': ' + token);
33 params.put('payment_method', token);
34 paymentMethodType = determinePaymentMethodTypeForSaleFlow(saleRequest);
35 params.put('payment_method_types[]', paymentMethodType);
36 // populate mandate data
37 populateMandateData(params, saleRequest.ipAddress);
38 }
39 // set customer id in params if it is available
40 if (saleRequest.paymentMethodData != null) {
41 String gatewayCustomerReference = saleRequest.paymentMethodData.get('gatewayReference');
42 if (gatewayCustomerReference != null) {
43 params.put('customer', gatewayCustomerReference);
44 }
45 // Set paymentMethodSubType to be used for Card for Link, Gpay, ApplePay
46 String paymentMethodSubType = saleRequest.paymentMethodData.get('paymentMethodSubType');
47 if (paymentMethodSubType != null && paymentMethodSubType.equals('link')) {
48 String existing = params.get('payment_method_types[]');
49 if (existing != null) {
50 params.put('payment_method_types[]', existing + '&payment_method_types[]=link');
51 } else {
52 params.put('payment_method_types[]', 'link');
53 }
54 }
55 }
56 String idempotencyKey = saleRequest.idempotencyKey;
57 Map < String, String > headers = new Map < String, String > ();
58 if (idempotencyKey != null) {
59 headers.put('Idempotency-Key', idempotencyKey);
60 }
61 String currencyIsoCode = saleRequest.currencyIsoCode;
62 String amount = toStripeCurrencyUnits(currencyIsoCode, saleRequest.amount);
63 params.put('amount', amount);
64 params.put('currency', currencyIsoCode);
65 params.put('confirm', 'true'); // create & authorize
66 params.put('capture_method', 'automatic'); // Set capture method to automatic
67 if (paymentMethodType == 'affirm') { // Adding support extended_apm . Affirm supported only for online session
68 params.put('return_url', 'https://example.com/success');
69 } else {
70 params.put('off_session', 'true');
71 }
72 HttpResponse response = doPost('payment_intents?expand[]=latest_charge', params, headers);
73 String body = response.getBody();
74 Map < String, Object > intent = (Map < String, Object > ) JSON.deserializeUntyped(body);
75 commercepayments.SaleResponse saleResponse = new commercepayments.SaleResponse();
76 saleResponse.setGatewayDate(system.now());
77 Integer sc = response.getStatusCode();
78 if (sc >= 200 && sc < 300) {
79 String status = (String) intent.get('status');
80 saleResponse.setGatewayResultCode(status);
81 saleResponse.setGatewayResultCodeDescription(status);
82 saleResponse.setGatewayReferenceNumber((String) intent.get('id'));
83 saleResponse.setSalesforceResultCodeInfo(RC_SUCCESS);
84 saleResponse.setAmount(fromStripeCurrencyUnits(currencyIsoCode, (Long) intent.get('amount')));
85 if (status.equals('processing')) {
86 saleResponse.setAsync(true);
87 }
88 } else {
89 // Handle errors
90 Map < String, Object > error = (Map < String, Object > ) intent.get('error');
91 String errorCode = (String) error.get('code');
92 String errorType = (String) error.get('type');
93 if (errorType.equals('card_error') && DECLINE_CODES.contains(errorCode)) {
94 populateErrorResponse(saleResponse, errorCode, error);
95 commercepayments.RetryCategory retryCategory = ERROR_CODES_RETRY_CATEGORY_MAP.get(errorCode);
96 saleResponse.setRetryCategory(retryCategory);
97 if (RETRIABLE_CODES.contains(errorCode)) {
98 saleResponse.setRetryDecision(commercepayments.RetryDecision.Retriable);
99 } else if (NON_RETRIABLE_CODES.contains(errorCode)) {
100 saleResponse.setRetryDecision(commercepayments.RetryDecision.NonRetriable);
101 }
102 } else {
103 return new commercepayments.GatewayErrorResponse(String.valueOf(sc), 'ERROR: ' + body);
104 }
105
106
107 }
108
109
110 return saleResponse;
111}
112private String determinePaymentMethodTypeForSaleFlow(commercepayments.SaleRequest saleRequest) {
113 String paymentMethodType = saleRequest.paymentMethodData.get('paymentMethodType');
114
115
116 // Adding support for extended payment method types
117 if ((paymentMethodType.equals('extd_wallet') || paymentMethodType.equals('extd_apm_type')) && saleRequest.paymentMethodData.get('extendedPaymentMethodType') != null) {
118 paymentMethodType = saleRequest.paymentMethodData.get('extendedPaymentMethodType');
119 }
120 return paymentMethodType;
121}