Query Saved Payment Method Records Using Apex

You can query Saved Payment Method records in your Salesforce org using Apex. This option is applicable only to third-party payment gateways.

Here’s an example of an Apex query to retrieve SPM records.

1SavedPaymentMethod savedPaymentMethod = fetchSavedPaymentMethod(idToProcess);
2token = savedPaymentMethod.GatewayToken;
3shopperReference = savedPaymentMethod.GatewayReference;
4String paymentMethodType = savedPaymentMethod.type;
5String extendedPaymentMethodType = savedPaymentMethod.ExtendedPaymentMethodType;
6System.debug('Queried SPM record for QA override');
7type = determinePaymentType(paymentMethodType, extendedPaymentMethodType, paymentTypeMap);
8private static SavedPaymentMethod fetchSavedPaymentMethod(Id idToProcess) {
9    QueryUtils q = new QueryUtils(SavedPaymentMethod.SObjectType);
10    q.getSelectClause().addField('GatewayToken', false);
11    q.getSelectClause().addField('GatewayReference', false);
12    q.getSelectClause().addField('ExtendedPaymentMethodType', false);
13    q.getSelectClause().addField('type', false);
14    q.setWhereClause(' WHERE Id = \'' + idToProcess + '\'');
15    return (SavedPaymentMethod) Database.query(q.buildSOQL())[0];
16}

Here’s a sample Apex snippet that demonstrates how to make gateway calls using the queried SPM records.

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}

See Also