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