CommercePayments 命名空间的用例

查看平台的演练、用例和参考资料。

CommercePayments

若要查看类参考文档,请转到 CommercePayments 命名空间。CommercePayments

  • 支付网关适配器 支付网关适配器
    代表了 Salesforce 中的支付平台与外部支付网关之间的桥梁。
  • 付款授权撤销服务
    授权撤销是通过解除客户付款方式中的资金冻结来否定授权的交易。
  • 令牌化服务
    信用卡令牌化过程将敏感的客户信息替换为在支付交易期间使用的一次性算法生成的数字(称为令牌)。Salesforce 存储令牌,然后使用该令牌作为用于交易的信用卡的表示形式。该令牌允许您存储有关信用卡的信息,而无需在 Salesforce 中存储敏感的客户数据,例如信用卡号。
  • 替代付款方式 替代付款方式允许客户存储和表示其他预定义的付款方式(如 或)未表示的付款方式
    信息。替代付款方式的常见示例包括 CashOnDeliver、Klarna 和直接借记。API v51.0 及更高版本中提供了其他付款方式。CardPaymentMethodDigitalWallet
  • 处理付款 在支付网关中处理付款
  • 处理退款 在支付网关中
    处理退款
  • 等性准则
    幂等性表示支付网关能够识别错误或恶意提交的重复请求,然后相应地处理重复请求。使用幂等网关时,请考虑以下重要准则。
  • CommercePayments 的示例支付网关实现 我们创建了一个 GitHub 存储库,其中包含具有 CommercePayments
    命名空间的示例 Payeezy 支付网关实现的代码示例。如果您在配置支付网关实施方面需要帮助,请查看示例代码。

支付网关适配器

支付网关适配器代表了您的支付平台之间的桥梁 Salesforce 和外部支付网关。

  • 构建同步网关适配器
    在同步付款配置中,Salesforce 付款平台将交易信息发送到网关,然后等待包含最终交易状态的网关响应。只有当事务在网关中成功时,Salesforce 才会创建事务。
  • 设置同步支付网关适配器
    对于支付交易,您可以将 Salesforce 配置为与同步支付网关适配器交互。
  • 构建异步网关适配器
    异步支付配置中,支付平台首先向网关发送交易信息。网关通过确认它已收到交易进行响应,然后平台创建待处理交易。网关发送通知,其中包含最终事务状态。然后,平台会相应地更新交易状态。
  • 设置异步支付网关适配器
    对于支付交易,您可以将 Salesforce 配置为与异步支付网关适配器交互。
  • 支付网关适配器的构建器示例 支付网关适配器的最后部分应定义适配器
    如何创建请求和响应。这些类的实现可能会因网关和平台要求而有很大差异。我们提供了几个泛型示例供查看。

构建同步网关适配器

在同步支付配置中,Salesforce 支付平台发送 事务信息发送到网关,然后等待包含 最终交易状态。Salesforce 仅在交易发生时才会创建交易 在网关中成功。

同步网关适配器实现 PaymentGatewayAdapter 接口。在本主题中,我们将分解一个示例异步 适配器,然后查看 类,它驱动了大部分 支付平台和支付网关之间的通信。PaymentGatewayAdapterprocessRequest

注意

支付网关适配器无法进行将来的调用、外部调用、异步调用、可排队的调用或执行 DML 使用 SOQL。System.Http

PaymentGateway适配器

所有同步网关都必须实现该接口。所有 PaymentGatewayAdapters 都是 实现该方法所必需的。

PaymentGatewayAdapterprocessRequest

global with sharing class SampleAdapter implements commercepayments.PaymentGatewayAdapter {
    global SampleAdapter() {}
    
    global commercepayments.GatewayResponse processRequest(commercepayments.paymentGatewayContext gatewayContext) {
    }
}

处理初始付款请求

当支付平台 接收付款 API 请求,并将请求传递给网关适配器 进一步评估。适配器通过调用 processRequest 方法开始请求评估过程,该方法表示同步的第一步 付款流程。我们可以将 processRequest 实现分为三个部分。

首先,它构建一个网关可以 理解。

commercepayments.RequestType requestType = gatewayContext.getPaymentRequestType();
if (requestType == commercepayments.RequestType.Capture) {
   req.setEndpoint('/pal/servlet/Payment/v52/capture');
    body = buildCaptureRequest((commercepayments.CaptureRequest)gatewayContext.getPaymentRequest());
} else if (requestType == commercepayments.RequestType.ReferencedRefund) {
    req.setEndpoint('/pal/servlet/Payment/v52/refund');
    body = buildRefundRequest((commercepayments.ReferencedRefundRequest)gatewayContext.getPaymentRequest());
}

然后 适配器将请求发送到付款 网关。

req.setBody(body);
req.setMethod('POST');
commercepayments.PaymentsHttp http = new commercepayments.PaymentsHttp();
HttpResponse res = null;
try {
    res = http.send(req);
} catch(CalloutException ce) {
    commercepayments.GatewayErrorResponse error = new commercepayments.GatewayErrorResponse('500', ce.getMessage());
    return error;
}

最后 适配器创建一个响应对象来存储来自网关响应的数据。这 响应对象的类型将根据您最初是否进行付款捕获而有所不同 申请或退款 请求。

if ( requestType == commercepayments.RequestType.Capture) {
   // Refer to the end of this doc for sample createCaptureResponse implementation
    response =  createCaptureResponse(res);
} else if ( requestType == commercepayments.RequestType.ReferencedRefund) {
    response =  createRefundResponse(res);
}
return response;

设置同步支付网关适配器

对于付款交易,您可以将 Salesforce 配置为与同步 支付网关适配器。

要访问 API,您需要 PaymentPlatform 组织权限。commercepayments

  1. 创建您的支付网关适配器 Apex 类。有关说明,请参阅构建同步网关适配器。
  2. 创建命名凭据。
    1. 在“设置”的“快速查找”框中,输入“命名凭据”,然后 然后选择新建。
    2. 填写必填字段,包括支付网关的 URL。
  3. 创建支付网关提供商。PaymentGatewayProvider 对象存储有关以下内容的详细信息 Salesforce Payments 在处理 交易。
    1. 根据以下方式连接以连接 REST API 中的说明生成访问令牌 OAuth。响应包括属性中指定的访问令牌和属性中指定的服务器实例。使用此信息进行 API 调用 构建支付网关提供商。access_tokeninstance_url
    2. 使用instance_url中的域对资源执行 POST 调用。例如,https:// instance_name.my.salesforce.com/services/data/v api_version/tooling/sobjects/PaymentGatewayProvider。使用此有效负载作为请求正文,替换为 正确的数据。value{ "ApexAdapterId": "value", "DeveloperName": "value", "MasterLabel": "value", "IdempotencySupported": "value", "Comments": "value" } Example: { "ApexAdapterId": "01pxx0000004UU8AAM", "DeveloperName": "MyNewGatewayProvider", "MasterLabel": "My New Gateway Provider", "IdempotencySupported": "Yes", "Comments": "Custom made gateway provider." }
  4. 创建支付网关记录。PaymentGateway 对象存储有关 连接到外部支付网关。记录需要这些字段值。
    • 支付网关名称:外部支付网关的名称。
    • 商家凭证 ID:您创建的指定凭证的 ID。
    • 支付网关提供商 ID:您拥有的支付网关提供商的 ID 创建。
    • 状态: 已上市

构建异步网关适配器

在异步支付配置中,支付平台首先发送 交易信息发送到网关。网关通过确认它进行响应 收到交易,然后平台创建一个待处理的交易。网关 发送通知,其中包含最终交易状态。然后,平台将更新 交易的状态。

异步过程与同步事务不同,在同步事务中,平台这样做 在初始网关请求之后不创建挂起的事务。取而代之的是, 平台仅在网关发送包含 最终交易状态。有关构建同步适配器的信息,请查看构建同步网关适配器。异步配置需要同步网关适配器和 异步适配器。在本主题中,我们将按以下方式分解示例异步适配器 着眼于几个重要领域。

  • 定义异步支付网关适配器
  • 处理初始付款请求
  • 处理来自支付网关的通知
  • 使用系统调试日志调试网关响应。

注意

支付网关适配器无法进行将来的调用、外部标注、异步调用、可排队的调用或 使用 SOQL 执行 DML。System.Http

异步支付网关适配器定义

异步网关适配器类必须同时实现 PaymentGatewayAdapter 接口和 PaymentGatewayAsyncAdapter 接口。适配器类 还必须实现 PaymentGatewayAdapter 的方法和 PaymentGatewayAsyncAdapter 的方法。processRequestprocessNotification

global with sharing class SampleAdapter implements commercepayments.PaymentGatewayAsyncAdapter, commercepayments.PaymentGatewayAdapter {
    global SampleAdapter() {}
    
    global commercepayments.GatewayResponse processRequest(commercepayments.paymentGatewayContext gatewayContext) {
    }
    
    global commercepayments.GatewayNotificationResponse processNotification(commercepayments.PaymentGatewayNotificationContext gatewayNotificationContext) {
    }
}

处理初始付款请求

当支付平台 接收付款 API 请求,并将请求传递给网关适配器 进一步评估。适配器通过调用 processRequest 方法开始请求评估过程,该方法表示异步的第一步 付款流程。我们可以将 processRequest 实现分为三个部分。

首先,它构建一个网关可以 理解。

commercepayments.RequestType requestType = gatewayContext.getPaymentRequestType();
if (requestType == commercepayments.RequestType.Capture) {
   req.setEndpoint('/pal/servlet/Payment/v52/capture');
    body = buildCaptureRequest((commercepayments.CaptureRequest)gatewayContext.getPaymentRequest());
} else if (requestType == commercepayments.RequestType.ReferencedRefund) {
    req.setEndpoint('/pal/servlet/Payment/v52/refund');
    body = buildRefundRequest((commercepayments.ReferencedRefundRequest)gatewayContext.getPaymentRequest());
}

然后 适配器将请求发送到付款 网关。

req.setBody(body);
req.setMethod('POST');
commercepayments.PaymentsHttp http = new commercepayments.PaymentsHttp();
HttpResponse res = null;
try {
    res = http.send(req);
} catch(CalloutException ce) {
    commercepayments.GatewayErrorResponse error = new commercepayments.GatewayErrorResponse('500', ce.getMessage());
    return error;
}

最后 适配器创建一个响应对象来存储来自网关响应的数据。这 响应对象的类型将根据您最初是否进行付款捕获而有所不同 申请或退款 请求。

if ( requestType == commercepayments.RequestType.Capture) {
   // Refer to the end of this doc for sample createCaptureResponse implementation
    response =  createCaptureResponse(res);
} else if ( requestType == commercepayments.RequestType.ReferencedRefund) {
    response =  createRefundResponse(res);
}
return response;

处理来自支付网关的通知

之后 客户银行处理交易并将结果发送到网关, 网关向适配器发送通知,指示它已准备好提供 最终交易状态。对于异步事务流的这一部分, 适配器需要调用 processNotification 类。我们可以拆分 processNotification 实现分为四个部分。

一、适配器 验证通知请求中的签名。欲了解更多信息 验证签名,审核 [主题]。

private Boolean verifySignature(NotificationRequest requestItem) {
    String payload = requestItem.pspReference + ':'
        + (requestItem.originalReference == null ? '' : requestItem.originalReference) + ':'
        + requestItem.merchantAccountCode + ':'
        + requestItem.merchantReference + ':'
        + requestItem.amount.value.intValue() + ':'
        + requestItem.amount.currencyCode + ':'
        + requestItem.eventCode + ':'
        + requestItem.success;
    String myHMacKey = getHMacKey();
    String generatedSign = EncodingUtil.base64Encode(Crypto.generateMac('hmacSHA256', Blob.valueOf(payload), 
                                EncodingUtil.convertFromHex(myHMacKey)));
    return generatedSign.equals(requestItem.additionalData.hmacSignature);
}

下一个 适配器分析网关的通知请求并生成通知对象。 方法 评估来自网关的通知请求项的数据,其中包括状态、 referenceNumber、event 和 amount。该对象设置为“成功”或“失败”,具体取决于 平台已成功收到通知。如果通知的事件代码 表示网关处理了付款捕获事务,适配器将构建一个 通知对象。如果事件代码指示网关 处理退款事务时,适配器使用该类生成通知对象。

getPaymentGatewayNotificationRequestnotificationStatusCaptureNotificationReferencedRefundNotification

commercepayments.PaymentGatewayNotificationRequest gatewayNotificationRequest = gatewayNotificationContext.getPaymentGatewayNotificationRequest();
Blob request = gatewayNotificationRequest.getRequestBody();
SampleNotificationRequest notificationRequest = SampleNotificationRequest.parse(request.toString().replace('currency', 'currencyCode'));                
List<SampleNotificationRequest.NotificationItems> notificationItems = notificationRequest.notificationItems;
SampleNotificationRequest.NotificationRequestItem notificationRequestItem = notificationItems[0].NotificationRequestItem;

Boolean success = Boolean.valueOf(notificationRequestItem.success);
String pspReference = notificationRequestItem.pspReference;
String eventCode = notificationRequestItem.eventCode;
Double amount = notificationRequestItem.amount.value;

commercepayments.NotificationStatus notificationStatus = null;
if (success) {
    notificationStatus = commercepayments.NotificationStatus.Success;
} else {
    notificationStatus = commercepayments.NotificationStatus.Failed;
}
commercepayments.BaseNotification notification = null;
if ('CAPTURE'.equals(eventCode)) {
    notification = new commercepayments.CaptureNotification();
} else if ('REFUND'.equals(eventCode)) {
    notification = new commercepayments.ReferencedRefundNotification();
}
notification.setStatus(notificationStatus);
notification.setGatewayReferenceNumber(pspReference);
notification.setAmount(amount);

这 然后,适配器请求支付平台记录 通知。

commercepayments.NotificationSaveResult saveResult = commercepayments.NotificationClient.record(notification);

都 异步网关要求平台确认它收到了 通知,无论平台是否成功保存了通知的 数据。平台调用该类来发送 确认。

GatewayNotificationResponse

commercepayments.GatewayNotificationResponse gnr = new commercepayments.GatewayNotificationResponse();
if (saveResult.isSuccess()) {
    system.debug('Notification accepted by platform');
} else {
    system.debug('Errors in the result '+ Blob.valueOf(saveResult.getErrorMessage()));
}
gnr.setStatusCode(200);
gnr.setResponseBody(Blob.valueOf('[accepted]'));
return gnr;

调试

通常,Apex 调试日志可在开发人员控制台中找到。但是,Salesforce 不会在开发人员控制台中存储来自该方法的调试日志。查看此部件 ,查看收集来宾用户的调试日志 设置调试日志记录部分。processNotification

设置异步支付网关适配器

对于支付交易,您可以将 Salesforce 配置为与 异步支付网关适配器。

要访问 API,您需要 PaymentPlatform 组织权限。commercepayments

  1. 创建 Salesforce 站点。在“设置”的“快速查找”框中,输入“站点”。在“站点和域”下,选择“站点”,请参阅设置 Salesforce 站点。将网站的公共访问设置设置为访客访问付款 API接口。
  2. 创建您的支付网关适配器 Apex 类。异步支付网关需要 实现异步适配器和同步适配器。有关建筑物的信息 网关适配器,请参阅构建异步网关适配器和构建同步网关适配器。
  3. 在 UI 中创建命名凭据。
    1. 在“设置”的“快速查找”框中,输入“命名凭据”,然后 ,然后选择新建
    2. 填写必填字段。对于 URL,请输入支付网关的 URL。
  4. 创建支付网关提供商。PaymentGatewayProvider 对象存储有关以下内容的详细信息 Salesforce Payments 在处理交易时与之通信的支付网关。
    1. 根据以下方式连接以连接 REST API 中的说明生成访问令牌 OAuth。响应包括属性中指定的访问令牌和属性中指定的服务器实例。使用此信息制作 API 调用以构建支付网关提供程序。access_tokeninstance_url
    2. 使用instance_url中的域对资源执行 POST 调用。例如,https:// instance_name.my.salesforce.com/services/data/v api_version/tooling/sobjects/PaymentGatewayProvider。使用此有效负载作为请求正文,替换为 正确的数据。value{ "ApexAdapterId": "value", "DeveloperName": "value", "MasterLabel": "value", "IdempotencySupported": "value", "Comments": "value" } Example: { "ApexAdapterId": "01pxx0000004UU8AAM", "DeveloperName": "MyNewGatewayProvider", "MasterLabel": "My New Gateway Provider", "IdempotencySupported": "Yes", "Comments": "Custom made gateway provider." }
  5. 创建支付网关记录。PaymentGateway 对象存储有关 连接到外部支付网关。记录需要这些字段值。
    • 支付网关名称:外部支付网关的名称。
    • Merchant Credential ID:您拥有的指定凭据的 ID 创建。
    • 支付网关提供商 ID:支付网关提供商的 ID 您创建的。
    • 状态: 已上市
  6. 通过在 外部支付网关。外部支付网关使用 Webhook 发送通知, 作为 HTTP POST 消息,发送到异步支付网关适配器。Webhook 是站点终端节点与支付网关 ID 的组合 供应商。
    1. 将以下 URL 用于您站点的端点,将 domain 替换为您站点的域和 URL。例如:https://MyDomainName.my.salesforce-sites.com/solutions/services/data/v58.0/commerce/payments/notify注意如果你不是 使用增强型域时,您组织的 Salesforce 站点 URL 会有所不同。有关详细信息,请参阅我的 Salesforce 帮助中的域 URL 格式。
    2. 查找支付网关提供商的 ID,并将 ?provider=ID 查询参数附加到终端节点。例如,https://MyDomainName.my.salesforce-sites.com/solutions/services/data/v58.0/commerce/payments/notify?provider=0cJR00000004CEhMAM
    3. 在外部支付网关的标准通知中输入 Webhook 设置。

支付网关适配器的生成器示例

支付网关适配器的最后部分应定义适配器的创建方式 请求和响应。这些类的实现可能会因 网关和平台要求。我们提供了几个泛型示例 回顾。

buildCapture请求

private String buildCaptureRequest(commercepayments.CaptureRequest captureRequest) {
   Boolean IS_MULTICURRENCY_ORG = UserInfo.isMultiCurrencyOrganization();
    QueryUtils qBuilderForAuth = new QueryUtils(PaymentAuthorization.SObjectType);
    qBuilderForAuth.getSelectClause().addField('GatewayRefNumber', false);
    qBuilderForAuth.setWhereClause(' WHERE Id =' + '\'' + captureRequest.paymentAuthorizationId + '\'');
    PaymentAuthorization authObject = (PaymentAuthorization)Database.query(qBuilderForAuth.buildSOQL())[0];

    JSONGenerator jsonGeneratorInstance = JSON.createGenerator(true);
    jsonGeneratorInstance.writeStartObject();
    jsonGeneratorInstance.writeStringField('merchantAccount', '{!$Credential.Username}');
    jsonGeneratorInstance.writeStringField('originalReference', authObject.GatewayRefNumber);

    jsonGeneratorInstance.writeFieldName('modificationAmount');
    jsonGeneratorInstance.writeStartObject();
    jsonGeneratorInstance.writeStringField('value', String.ValueOf((captureRequest.amount * 100.0).intValue()));
    jsonGeneratorInstance.writeEndObject();

    jsonGeneratorInstance.writeEndObject();
    return jsonGeneratorInstance.getAsString();
}

createCaptureResponse

private commercepayments.GatewayResponse createCaptureResponse(HttpResponse response) {
    Map<String, Object> mapOfResponseValues = (Map
                        <String, Object>) JSON.deserializeUntyped(response.getBody());
    Integer statusCode = response.getStatusCode();
    String responceValue = (String)mapOfResponseValues.get('response');
    if(statusCode == 200) {
        system.debug('Response - success - Capture received');
       commercepayments.CaptureResponse captureResponse = new commercepayments.CaptureResponse();
        captureResponse.setAsync(true); // Very important to treat this as an asynchronous transaction
        captureResponse.setGatewayReferenceNumber((String)mapOfResponseValues.get('pspReference'));
        captureResponse.setSalesforceResultCodeInfo(new commercepayments.SalesforceResultCodeInfo(commercepayments.SalesforceResultCode.Success));
        return captureResponse;
    } else {
        system.debug('Response - error - Capture not received by Gateway');
        String message = (String)mapOfResponseValues.get('message');
        commercepayments.GatewayErrorResponse error = new commercepayments.GatewayErrorResponse(String.valueOf(statusCode), message);
        return error;
    }
}

付款授权撤销服务

授权撤销是通过释放来否定授权的交易 客户付款方式中的资金冻结。

  • 授权反转Apex类实现
    授权反转服务使用 和 类来管理授权反转信息的创建和存储。在支付网关适配器中实现这些类。AuthorizationReversalRequestAuthorizationReversalResponse
  • 支付授权撤销服务 API
    授权撤销是指通过解除客户支付方式中的资金冻结来否定授权的交易。使用授权撤销服务,为用户提供撤销未完成的付款授权的能力。

授权撤销 Apex 类实现

授权撤销服务使用 和 类来管理 授权撤销信息。在支付网关中实现这些类 适配器。

AuthorizationReversalRequestAuthorizationReversalResponseAuthorizationReversal请求表示授权撤销请求。扩展并继承其所有方法。BaseRequestAuthorizationReversalRequest使用构造函数 在 Salesforce 中建立授权撤销请求记录。构造函数不带任何参数。 您可以按如下方式调用它。AuthorizationReversalRequest

CommercePayments.AuthorizationReversalRequest arr = new CommercePayments.AuthorizationReversalRequest();

如果要生成示例授权撤销,还可以使用 冲销金额和付款授权 ID 的参数。但是,构造函数会 仅适用于测试使用,如果在 Apex 测试之外使用,则会引发异常 上下文。

commercepayments.AuthorizationReversalRequest authorizationReversalRequest = 
new commercepayments.AuthorizationReversalRequest(80, authObj.id);

AuthorizationReversalResponse支付网关适配器发送此类作为授权撤销的响应 请求类型。扩展并继承其 方法。AbstractResponseAuthorizationReversalResponse使用构造函数 在 Salesforce 中建立授权撤销请求记录。构造函数不带任何参数。 您可以按如下方式调用它:AuthorizationReversalResponse

CommercePayments.AuthorizationReversalResponse arp = new CommercePayments.AuthorizationReversalResponse();

注意

Salesforce 不支持批量操作或授权撤销中的自定义字段 过程。

在网关适配器中实现反转类

将您的反转类添加到您的支付网关适配器。我们建议在调用网关的响应时添加一个可能的值。AuthorizationReversalrequestTypeprocessRequest

global commercepayments.GatewayResponse processRequest(commercepayments.paymentGatewayContext gatewayContext) {
        commercepayments.RequestType requestType = gatewayContext.getPaymentRequestType();
        commercepayments.GatewayResponse response;
        
        try {
        //add conditions for other requestType values here
        //..
        else if (requestType == commercepayments.RequestType.AuthorizationReversal) {
                response = createAuthReversalResponse((commercepayments.AuthorizationReversalRequest)gatewayContext.getPaymentRequest());}
        
        return response;

然后,添加一个设置授权撤销请求量的类 gateway 信息和 Salesforce 结果代码。

global commercepayments.GatewayResponse createAuthReversalResponse(commercepayments.AuthorizationReversalRequest authReversalRequest) {
        commercepayments.AuthorizationReversalResponse authReversalResponse = new commercepayments.AuthorizationReversalResponse();
        if(authReversalRequest.amount!=null )
        {
            authReversalResponse.setAmount(authReversalRequest.amount);
        }
        else
        {
             throw new SalesforceValidationException('Required Field Missing : Amount');             
        }
   
        system.debug('Response - success');
        authReversalResponse.setGatewayDate(system.now());
        authReversalResponse.setGatewayResultCode('00');
        authReversalResponse.setGatewayResultCodeDescription('Transaction Normal');
        //Replace 'xxxxx' with the gateway reference number.
        authReversalResponse.setGatewayReferenceNumber('SF'+xxxxx);
        authReversalResponse.setSalesforceResultCodeInfo(SUCCESS_SALESFORCE_RESULT_CODE_INFO);
        return authReversalResponse;
    }

示例 Apex 请求

String authorizationId = '0XcxXXXXXXXXXXXXXXX';
ConnectApi.AuthorizationReversalRequest authorizationReversalRequest = new ConnectApi.AuthorizationReversalRequest();
authorizationReversalRequest.amount = 1.0;
authorizationReversalRequest.comments = 'Captured from custom action';
authorizationReversalRequest.ipAddress = '192.162.10.3';
authorizationReversalRequest.email = 'testuser@example.com';

ConnectApi.AuthorizationReversalResponse authorizationReversalResponse = ConnectApi.Payments.reverseAuthorization(authorizationReversalRequest, authorizationId);
String authReversalId = authorizationReversalResponse.paymentAuthAdjustment.id;
System.debug(authorizationReversalResponse);
System.debug(authReversalId);

支付授权撤销服务API

授权撤销是通过释放来否定授权的交易 客户付款方式中的资金冻结。使用授权撤销服务可以 使用户能够撤销未完成的付款授权。

有时,客户执行付款授权,但随后需要取消全部或部分 稍后授权。例如,客户购买了三件商品,然后意识到 第一件商品已经在他们的库存中。Commerce Payments API 允许您撤消全部或部分 未完成的付款授权。

客户支付网关授权付款后,Commerce Payments 将创建付款 授权记录,用于存储有关授权的信息。当用户或进程 对授权执行撤销,授权撤销服务会创建一个 对存储信息进行支付授权调整。该调整与 授权。如果付款授权与订单付款摘要相关联,则撤销 金额将添加到订单付款摘要的 AuthorizationReversalAmount 中,并从其 AvailableToCaptureAmount 中减去。但 AvailableToCaptureAmount 永远不会低于 0,即使反转使其 计算负数。

注意

对于授权撤销,支付网关日志的 OrderPaymentSummaryId 始终默认为 null。如果有一个 关联订单付款汇总,您的代码可以设置值。

通过向以下终结点发出 POST 请求来调用授权撤销服务。

端点

/commerce/payments/authorizations/${*authorizationId*}/reversals

该服务每次调用接受一个授权撤销请求。以下付款 接受授权调整 API 参数。

参数必填描述
必填从授权中冲销的金额。必须大于零。Salesforce 不提供与 相比的验证。PaymentAuthorizationAdjustment.AmountPaymentAuthorization.Amount如果支付网关允许的撤销金额大于授权金额 金额,则授权的结果余额可以为负数。如果您的网关支持 授权余额低于零,并且您希望避免网关调用,请配置 适配器查询授权金额、余额和总冲销金额,不查询 如果余额小于零,则调用终结点。
帐户 ID自选此授权撤销链接到的帐户 ID。
生效日期自选撤销适用于授权的日期。
电子邮件自选欺诈参数
ip地址自选欺诈参数
mac地址自选欺诈参数
电话自选欺诈参数
评论自选用户提供的有关授权撤销的注释。必须小于 1000 字符。

示例请求和响应

此请求调用针对授权的 150 美元撤销。

{
  "accountId":"",
  "amount": "150",*  "comments": "authorization reversal request",
  "effectiveDate":"2020-10-18T11:32:27.000Z",
  "ipAddress": "202.95.77.70",
  "macAddress": "00-14-22-01-23-45",
  "phone": "100-456-67",
  "email": "test@example.org",
  "additionalData":{
       //add additional parameters if needed
      "key1":"value1",
      "key2":"value2",
      "key3":"value3",
      "key4":"value4",
      "key5":"value5"
    }
}

示例响应 – 成功

成功的授权撤销响应提供有关网关响应的信息 以及用于构造付款授权调整实体的值。

HPP Status Code: 201
{
  "gatewayResponse" : {
    "gatewayDate" : "2020-10-23T15:21:58.833Z",
    "gatewayReferenceNumber" : "439XXXXXXX",
    "gatewayResultCode" : "00",
    "gatewayResultCodeDescription" : "Transaction Normal",
    "salesforceResultCode" : "Success"
  },
  "paymentAuthAdjustment" : {
    "amount" : "150.0",
    "currencyIsoCode" : "USD",
    "effectiveDate" : "2020-10-18T11:32:27.000Z",
    "id" : "9tvR00000004Cf1MAE",
    "paymentAuthAdjustmentNumber" : "PAA-00XXXXXXX",
    "requestDate" : "2020-10-23T15:21:58.000Z",
    "status" : "Processed"
  },
  "paymentGatewayLogs" : [ {
    "createdDate" : "2020-10-23T15:21:58.000Z",
    "gatewayResultCode" : "00",
    "id" : "0XtXXXXXXXXXXXXXXX",
    "interactionStatus" : "Success"
  } ]
}

Salesforce 中生成的付款授权调整如下所示。

如果返回错误,则响应将包含网关的错误代码和错误消息。

示例响应 – 错误

{
    "errorCode":"",
    "errorMessage":""
}

代币化服务

信用卡令牌化过程将敏感的客户信息替换为 在支付交易期间使用的一次性算法生成的号码,称为令牌。 Salesforce 存储令牌,然后使用该令牌作为所用信用卡的表示形式 用于交易。令牌允许您存储有关信用卡的信息,而无需存储 Salesforce 中的敏感客户数据,例如信用卡号。

  • 令牌化服务 Apex 类实现
    使用令牌化服务隐藏敏感的客户付款方式数据。标记化服务使用 、 和 。在支付网关适配器中实现这些类。PaymentMethodTokenizationRequestPaymentMethodTokenizationResponseCardPaymentMethodRequest
  • 令牌化服务 API
    信用卡令牌化过程将敏感的客户信息替换为算法生成的一次性数字(称为令牌),以便在支付交易期间使用。Salesforce 存储令牌,然后使用该令牌作为用于交易的信用卡的表示形式。该令牌允许您存储有关信用卡的信息,而无需在 Salesforce 中实际存储敏感的客户数据(如信用卡号)。实施我们的令牌化 API,为您的支付服务添加令牌化功能。

令牌化服务 Apex 类实现

使用令牌化服务隐藏敏感的客户付款方式数据。这 标记化服务使用 、 和 。在支付网关中实现这些类 适配器。

PaymentMethodTokenizationRequestPaymentMethodTokenizationResponseCardPaymentMethodRequest

令牌化支付方式的加密

CommercePayments 使用 Salesforce 字段加密将网关令牌值安全地存储在 客户付款方式实体,例如 DigitalWallet、CardPaymentMethod 和 AlternativePaymentMethod。

CardPaymentMethod 和 DigitalWallet 包含 GatewayTokenEncrypted 字段,该字段在 API v52.0 及更高版本,以及 GatewayToken 字段,在 API v48.0 及更高版本中可用。双 字段存储网关令牌值。但是,GatewayTokenEncrypted 使用 Salesforce Classic Encryption for Custom 用于安全加密令牌的字段。GatewayToken 不使用加密。自 确保安全令牌化,我们建议在您的 DigitalWallets 上使用 GatewayTokenEncrypted 和 CardPaymentMethods。AlternativePaymentMethod 对象将 GatewayToken 字段用于 令牌存储,但是,此字段在 AlternativePaymentMethods 上加密。

在 API 版本 52.0 及更高版本中,CardPaymentMethods 和 DigitalWallets 无法存储 GatewayTokenEncryption 和 GatewayToken 同时位于同一记录上。如果您尝试 当另一个存在时分配一个,Salesforce 会抛出错误。

您的支付网关适配器使用 和 类来检索网关令牌 从支付网关,在 Salesforce 中对其进行加密,并将值存储在付款方式上 实体。让我们看看如何在支付网关适配器中配置这些类。PaymentMethodTokenizationRequestPaymentMethodTokenizationResponse

在网关适配器中实现标记化类

以下代码在 Apex 类中使用。PaymentGatewayAdapter

当类的方法收到 令牌化请求。如果请求类型为 ,则调用该方法 并传递类的实例。这 传递的对象包含地址和 cardPaymentMethod 支付网关管理令牌化所需的信息 过程。例如:GatewayResponseprocessRequestTokenizeGatewayResponsecreateTokenizeResponsePaymentMethodTokenizationRequestPaymentMethodTokenizationRequest

global commercepayments.GatewayResponse processRequest(commercepayments.paymentGatewayContext gatewayContext) {
         commercepayments.RequestType requestType = gatewayContext.getPaymentRequestType();
         commercepayments.GatewayResponse response;
         try
         {
             if (requestType == commercepayments.RequestType.Tokenize) {
                     response = createTokenizeResponse((commercepayments.PaymentMethodTokenizationRequest)gatewayContext.getPaymentRequest());
             }
             //Add other else if statements for different request types as needed.
             return response;
         }
         catch(SalesforceValidationException e)
         {
              commercepayments.GatewayErrorResponse error = new commercepayments.GatewayErrorResponse('400', e.getMessage());
              return error;
         }
     }

将方法配置为 接受 的实例,然后根据它收到的值生成 PaymentMethodTokenizationResponse 的实例 从支付网关。tokenizeResponse 包含网关的 标记化过程,如果成功,则标记化值。在此示例中,我们调用该方法来设置 标记化响应中的标记化值。createTokenizeResponsePaymentMethodTokenizationRequestsetGatewayTokenEncrypted

public commercepayments.GatewayResponse createTokenizeResponse(commercepayments.PaymentMethodTokenizationRequest tokenizeRequest) {
         commercepayments.PaymentMethodTokenizationResponse tokenizeResponse = new commercepayments.PaymentMethodTokenizationResponse();
         tokenizeResponse.setGatewayTokenEncrypted(encryptedValue);
         tokenizeResponse.setGatewayTokenDetails(tokenDetails);
         tokenizeResponse.setGatewayAvsCode(avsCode);
         tokenizeResponse.setGatewayMessage(gatewayMessage);
         tokenizeResponse.setGatewayResultCode(resultcode);
         tokenizeResponse.setGatewayResultCodeDescription(resultCodeDescription);
         tokenizeResponse.setSalesforceResultCodeInfo(resultCodeInfo);
         tokenizeResponse.setGatewayDate(system.now());
         return tokenizeResponse;
     }

该方法在 Salesforce API v52.0 及更高版本。它使用 Salesforce 经典加密来设置加密 可以存储在 CardPaymentMethod 上的 GatewayTokenEncrypted 中的令牌值,或者 DigitalWallet,或在 AlternativePaymentMethod 上的 GatewayToken 中。我们建议您使用来确保您的代币化付款 方法值是加密且安全的。setGatewayTokenEncryptedsetGatewayTokenEncrypted

/** @description Method to set Gateway token to persist in Encrypted Text */
     global void setGatewayTokenEncrypted(String gatewayTokenEncrypted) {
         if (gatewayTokenSet)  {
             throwTokenError();
         }
         this.delegate.setGatewayTokenEncrypted(gatewayTokenEncrypted);
         gatewayTokenEncryptedSet = true;
     }

如果实例化的类已具有网关令牌,则会引发错误。setGatewayTokenEncrypted

注意

虽然 PaymentMethodTokenizationResponse 的 setGatewayToken 方法(在 API v48.0 及更高版本中可用)也返回 付款方式令牌,则令牌化值未加密。

令牌化服务 API

信用卡令牌化过程将敏感的客户信息替换为 算法生成的一次性号码,称为令牌,用于支付交易期间。 Salesforce 存储令牌,然后使用该令牌作为所用信用卡的表示形式 用于交易。令牌允许您存储有关信用卡的信息,而无需实际 在 Salesforce 中存储敏感的客户数据,例如信用卡号。实施我们的 令牌化 API,用于为您的支付服务添加令牌化功能。

在典型的令牌化过程中,支付平台接受客户支付方式数据 并将其传递给 Salesforce 外部支付网关上的远程令牌服务服务器。 服务器为平台上的存储提供标记化值。例如,客户 提供 的信用卡号。令牌服务器存储 此值,将其与 的令牌相关联,并发送 用于在平台上存储的令牌。4111 1111 1111 12342537446225198291

在与商家通信期间,商家将令牌发送到令牌服务器。令牌服务器确认它 匹配客户的令牌,并授权商家针对 客户的卡。2537446225198291

Commerce Payments Tokenization API 接受信用卡信息并使用外部 通过客户的 Salesforce 组织配置的支付网关,用于对卡进行令牌化 信息。然后,它返回标记化表示形式。然后,API 将令牌保存在 中。CardPaymentMethod

通过向以下终结点发出 POST 请求来调用授权撤销服务。

端点

/commerce/payments/payment-method/tokens/

令牌化服务接受来自付款的以下请求参数,并相关 实体。

参数必填
cardPaymentMethod: { "cardHolderName":"", "expiryMonth":"", "expiryYear":"", "startMonth":"", "startYear":"", "cvv":"", "cardNumber":"", "cardCategory":"", "cardType":"", "nickName":"", "cardHolderFirstName":"", "cardHolderLastName":"", "email":"", "comments":"" }必填要令牌化的信用卡的详细信息。
帐户 ID自选持卡人的 Salesforce 帐户 ID。
"address":{ "street":"", "city":"", "state":"", "country":"", "postalCode":"", "companyName":"", }自选拥有信用卡付款方式的客户的地址信息 代币化。
paymentGatewayId必填与令牌化服务器相关的外部支付网关。
电子邮件自选欺诈参数。
ip地址自选欺诈参数。
mac地址自选欺诈参数。
电话自选欺诈参数。
additionalData自选网关令牌化信用卡付款所需的任何其他数据 方法。

示例请求和响应

此示例请求提供客户的信用卡信息以进行令牌化。

{
    "cardPaymentMethod": {
        "cardHolderName":"Carol Smith",
        "expiryMonth": "05",
        "expiryYear": "2025", 
        "startMonth": "",
        "startYear": "",
        "cvv": "000",
        "cardNumber": "4111111111111111",
        "cardCategory": "Credit",
        "cardType": "Visa",
        "nickName": "",
        "cardHolderFirstName": "Carol",
        "cardHolderLastName": "Smith",
        "email" : "csmith@example.com",
        "comments" : "",
        "accountId": "000XXXXXXXX"
    },
    "address":{
        "street": "128 1st Street",
        "city": "San Francisco",
        "state": "CA",
        "country": "USA",
        "postalCode": "94015",
        "companyName": "Salesforce"
    },
    "paymentGatewayId" : "000XXXXXXXX",
    "email": ""
    "ipAddress": "",
    "macAddress": "",
    "phone": "",
   
    "additionalData":{
         //add additional information if needed
        "key1":"value1",
        "key2":"value2",
        "key3":"value3",
        "key4":"value4",
        "key5":"value5"
    }
}

示例成功响应

成功的令牌化响应会更新付款方式,并提供以下信息 网关响应和任何支付网关日志。

{
  "paymentMethod": {
    "id": "03OR0000000xxxxxxx",
    "accountId" : "001xx000000xxxxxxx",
    "status" : "Active"
  },
  "gatewayResponse" : {
    "gatewayResultCode": "00",
    "gatewayResultCodeDescription": "Transaction Normal",
    "gatewayDate": "2020-12-08T04:03:20.000Z",
    "gatewayAvsCode" : "7638788018713617",
    "gatewayMessage" : "8313990738208498",
    "salesforceResultCode": "Success",
    "gatewayTokenEncrypted" : "SF701252"
  }
  "paymentGatewayLogs" : [ {
    "createdDate" : "2020-12-08T04:03:20.000Z",
    "gatewayResultCode" : "00",
    "id" : "0XtR0000000xxxxxxx",
    "interactionStatus" : "NoOp"
  } ],
}

其他付款方式

另一种付款方式允许客户存储和表示付款方式 未由其他预定义的付款方式(如 或 )表示的信息。替代支付方式的常见示例包括 CashOnDeliver、 Klarna 和直接借记。API v51.0 和 后。

CardPaymentMethodDigitalWallet

为组织中的每种替代付款方式创建唯一的记录类型。这边 每种替代付款方式都可以显示不同的选择列表值和页面布局 基于方法提供程序和网关提供程序的要求。例如,你可以有一个 直接借记的替代付款方式记录类型和现金支付的其他记录类型 提供。

我们还建议您为每种独特的替代付款方式记录类型创建一个。GtwyProviderPaymentMethodType

AlternativePaymentMethod 默认启用了私有共享模型,用于内部和 外部用户。只有记录所有者和具有较高所有权的用户才具有“读取”、“编辑”和“删除” 访问。

假设您想为 GiroPay 提供另一种付款方式。首先,创建记录类型。

AlternativePaymentMethod

新增功能 记录类型

/services/data/v51.0/sobjects/RecordType
{
 "Name" : "Giro Pay",
 "DeveloperName" : "GiroPay",
 "SobjectType" : "AlternativePaymentMethod"
}

下一个 为记录类型创建备用付款方式记录。AlternativePaymentMethod

新增功能 AlternativePaymentMethod

/services/data/v51.0/sobjects/AlternativePaymentMethod
{
 "ProcessingMode": "External",
 "status":"Active",
 "GatewayToken":"mHkDsh0oIA3mnWjo9UL",
"NickName" : "MyGiroPay",
"RecordTypeId" : "{record_type_id}"
}

您还可以创建网关提供商付款 方法类型。

新增功能 GtwyProvPaymentMethodType

{
 "PaymentGatewayProviderId": "XXXXXXXXXXXXXXX",
 "PaymentMethodType":"AlternativePaymentMethod",
 "GtwyProviderPaymentMethodType" : "PM_Giro",
 "DeveloperName" : "DevName",
 "MasterLabel" : "MasterLabel",
 "RecordTypeId" : "{record_type_id}"
}

处理付款

在支付网关中处理付款。

要访问 API,您需要 PaymentPlatform 组织 许可。

commercepayments

  1. 从 PaymentGatewayContext 类获取付款捕获请求对象。commercepayments.CaptureRequest = (commercepayments.CaptureRequest)gatewayContext.getPaymentRequest()
  2. 设置 HTTP 请求对象。HttpRequest req = new HttpRequest(); req.setHeader('Content-Type', 'application/json');
  3. 从 CaptureRequest 对象中读取参数并准备 HTTP 请求正文。
  4. 使用 PaymentsHttp 类对网关进行 HTTP 调用。commercepayments.PaymentsHttp http = new commercepayments.PaymentsHttp(); HttpResponse res = http.send(req);
  5. 分析 httpResponse 并准备 CaptureResponse 对象。commercepayments.CaptureResponse captureResponse = new commercepayments.CaptureResponse(); captureResponse.setGatewayResultCode(“”); captureResponse.setGatewayResultCodeDescription(“”); captureResponse.setGatewayReferenceNumber(“”); captureResponse.setSalesforceResultCodeInfo(getSalesforceResultCodeInfo(commercepayments.SalesforceResultCode.SUCCESS.name())); captureResponse.setGatewayReferenceDetails(“”); captureResponse.setAmount(double.valueOf(100);
  6. 返回 captureResponse

处理退款

在支付网关中处理退款。

要访问 API,您需要 PaymentPlatform 组织权限。

commercepayments

  1. 从 PaymentGatewayContext 类中获取引用的退款请求对象。commercepayments.ReferencedRefundRequest = (commercepayments.ReferencedRefundRequest)gatewayContext.getPaymentRequest();
  2. 设置 HTTP 请求对象。HttpRequest req = new HttpRequest(); req.setHeader('Content-Type', 'application/json');
  3. 从 ReferencedRefundRequest 对象中读取参数,并准备 HTTP 请求正文。
  4. 使用PaymentsHttp 类对网关进行 HTTP 调用。commercepayments.PaymentsHttp http = new commercepayments.PaymentsHttp(); HttpResponse res = http.send(req);
  5. 分析 httpResponse 并准备 ReferencedRefundResponse 对象。commercepayments.ReferencedRefundResponse referencedRefundResponse = new commercepayments.ReferencedRefundResponse(); referencedRefundResponse.setGatewayResultCode(“”); referencedRefundResponse.setGatewayResultCodeDescription(“”); referencedRefundResponse.setGatewayReferenceNumber(“”); referencedRefundResponse.setSalesforceResultCodeInfo(getSalesforceResultCodeInfo(commercepayments.SalesforceResultCode.SUCCESS.name())); referencedRefundResponse.setGatewayReferenceDetails(“”); referencedRefundResponse.setAmount(double.valueOf(100);
  6. 返回 referencedRefundResponse

幂等性准则

幂等性表示支付网关识别重复请求的能力 错误或恶意提交,然后相应地处理重复请求。 使用幂等网关时,请考虑以下重要准则。

要访问 API,您需要 PaymentPlatform 组织权限。

commercepayments

付款网关适配器类链接到 paymentGatewayProvider 对象记录。船级社 Payments 为自己的服务请求提供自己的幂等性层。每笔付款 网关还可以指定它们的值 在 paymentGatewayProvider 中 对象 记录。如果 Salesforce CCS Payment API 检测到重复请求和网关提供商 支持幂等性,请求正文的参数变为 .idempotencySupportedduplicateTrue

commercepayments.CaptureRequest request = 
(commercepayments.CaptureRequest)paymentGatewayContext.getPaymentRequest();
Boolean isDuplicate = requestObject.duplicate

幂等密钥可以从请求对象中获取。

String idempotencyKey = request.idempotencyKey

CommercePayments 的示例支付网关实施

我们创建了一个 GitHub 存储库,其中包含示例 Payeezy 付款的代码示例 具有 CommercePayments 命名空间的网关实现。如果需要,请查看示例代码 帮助配置支付网关实施。

查看 CommercePayments Gateway 参考实现中的代码示例,了解 Payeezy 存储库。