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.

SavedPaymentMethod savedPaymentMethod = fetchSavedPaymentMethod(idToProcess);
token = savedPaymentMethod.GatewayToken;
shopperReference = savedPaymentMethod.GatewayReference;
String paymentMethodType = savedPaymentMethod.type;
String extendedPaymentMethodType = savedPaymentMethod.ExtendedPaymentMethodType;
System.debug('Queried SPM record for QA override');
type = determinePaymentType(paymentMethodType, extendedPaymentMethodType, paymentTypeMap);
private static SavedPaymentMethod fetchSavedPaymentMethod(Id idToProcess) {
    QueryUtils q = new QueryUtils(SavedPaymentMethod.SObjectType);
    q.getSelectClause().addField('GatewayToken', false);
    q.getSelectClause().addField('GatewayReference', false);
    q.getSelectClause().addField('ExtendedPaymentMethodType', false);
    q.getSelectClause().addField('type', false);
    q.setWhereClause(' WHERE Id = \'' + idToProcess + '\'');
    return (SavedPaymentMethod) Database.query(q.buildSOQL())[0];
}

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

public commercepayments.GatewayResponse createSaleResponse(commercepayments.SaleRequest saleRequest) {
    // API: https://stripe.com/docs/api/payment_intents
    String token;
    String paymentMethodType;
    Map < String, String > params = new Map < String, String > ();
    Id idToProccess = saleRequest.paymentMethod.id;
    // payment method is card if payment method data is not present or paymentMethodType is card
    if (saleRequest.paymentMethodData == null) {
        // resolved card token,
        //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
        system.debug('Payment method data is null');
        String paymentMethodId = saleRequest.paymentMethod.id;
        if (idToProccess.getSObjectType() == CardPaymentMethod.SObjectType) {
            token = resolveCardToken(paymentMethodId);
        } else if (idToProccess.getSObjectType() == SavedPaymentMethod.SObjectType) {
            token = resolveSPMToken(paymentMethodId);
        }
        params.put('payment_method', urlEncode(token));
        params.put('payment_method_types[]', 'card');
    } else {
        //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).
        //paymentMethodData would have data in case of ACH
        system.debug('Payment method data is not null');
        if (saleRequest.amount == 999999 && idToProccess.getSObjectType() == SavedPaymentMethod.SObjectType) {
            // NOTE: This is only for QA purposes. In an ideal scenario, querying the database record should be avoided unless absolutely necessary.
            String paymentMethodId = saleRequest.paymentMethod.id;
            token = resolveSPMToken(paymentMethodId);
            system.debug('SPM query done when paymentMethodData was available');
        } else {
            token = saleRequest.paymentMethodData.get('gatewayToken');
        }
        System.debug('Value of gatewayToken ' + ': ' + token);
        params.put('payment_method', token);
        paymentMethodType = determinePaymentMethodTypeForSaleFlow(saleRequest);
        params.put('payment_method_types[]', paymentMethodType);
        // populate mandate data
        populateMandateData(params, saleRequest.ipAddress);
    }
    // set customer id in params if it is available
    if (saleRequest.paymentMethodData != null) {
        String gatewayCustomerReference = saleRequest.paymentMethodData.get('gatewayReference');
        if (gatewayCustomerReference != null) {
            params.put('customer', gatewayCustomerReference);
        }
        // Set paymentMethodSubType to be used for Card for Link, Gpay, ApplePay
        String paymentMethodSubType = saleRequest.paymentMethodData.get('paymentMethodSubType');
        if (paymentMethodSubType != null && paymentMethodSubType.equals('link')) {
            String existing = params.get('payment_method_types[]');
            if (existing != null) {
                params.put('payment_method_types[]', existing + '&payment_method_types[]=link');
            } else {
                params.put('payment_method_types[]', 'link');
            }
        }
    }
    String idempotencyKey = saleRequest.idempotencyKey;
    Map < String, String > headers = new Map < String, String > ();
    if (idempotencyKey != null) {
        headers.put('Idempotency-Key', idempotencyKey);
    }
    String currencyIsoCode = saleRequest.currencyIsoCode;
    String amount = toStripeCurrencyUnits(currencyIsoCode, saleRequest.amount);
    params.put('amount', amount);
    params.put('currency', currencyIsoCode);
    params.put('confirm', 'true'); // create & authorize
    params.put('capture_method', 'automatic'); // Set capture method to automatic
    if (paymentMethodType == 'affirm') { // Adding support extended_apm . Affirm supported only for online session
        params.put('return_url', 'https://example.com/success');
    } else {
        params.put('off_session', 'true');
    }
    HttpResponse response = doPost('payment_intents?expand[]=latest_charge', params, headers);
    String body = response.getBody();
    Map < String, Object > intent = (Map < String, Object > ) JSON.deserializeUntyped(body);
    commercepayments.SaleResponse saleResponse = new commercepayments.SaleResponse();
    saleResponse.setGatewayDate(system.now());
    Integer sc = response.getStatusCode();
    if (sc >= 200 && sc < 300) {
        String status = (String) intent.get('status');
        saleResponse.setGatewayResultCode(status);
        saleResponse.setGatewayResultCodeDescription(status);
        saleResponse.setGatewayReferenceNumber((String) intent.get('id'));
        saleResponse.setSalesforceResultCodeInfo(RC_SUCCESS);
        saleResponse.setAmount(fromStripeCurrencyUnits(currencyIsoCode, (Long) intent.get('amount')));
        if (status.equals('processing')) {
            saleResponse.setAsync(true);
        }
    } else {
        // Handle errors
        Map < String, Object > error = (Map < String, Object > ) intent.get('error');
        String errorCode = (String) error.get('code');
        String errorType = (String) error.get('type');
        if (errorType.equals('card_error') && DECLINE_CODES.contains(errorCode)) {
            populateErrorResponse(saleResponse, errorCode, error);
            commercepayments.RetryCategory retryCategory = ERROR_CODES_RETRY_CATEGORY_MAP.get(errorCode);
            saleResponse.setRetryCategory(retryCategory);
            if (RETRIABLE_CODES.contains(errorCode)) {
                saleResponse.setRetryDecision(commercepayments.RetryDecision.Retriable);
            } else if (NON_RETRIABLE_CODES.contains(errorCode)) {
                saleResponse.setRetryDecision(commercepayments.RetryDecision.NonRetriable);
            }
        } else {
            return new commercepayments.GatewayErrorResponse(String.valueOf(sc), 'ERROR: ' + body);
        }


    }


    return saleResponse;
}
private String determinePaymentMethodTypeForSaleFlow(commercepayments.SaleRequest saleRequest) {
    String paymentMethodType = saleRequest.paymentMethodData.get('paymentMethodType');


    // Adding support for extended payment method types
    if ((paymentMethodType.equals('extd_wallet') || paymentMethodType.equals('extd_apm_type')) && saleRequest.paymentMethodData.get('extendedPaymentMethodType') != null) {
        paymentMethodType = saleRequest.paymentMethodData.get('extendedPaymentMethodType');
    }
    return paymentMethodType;
}

See Also