Alipay, China's leading third-party online payment solutionAlipay, China's leading third-party online payment solution

服务端-服务端模式

本文介绍了如何通过服务器到服务器模式集成银行卡支付方法。这种集成方法适合对支付流程定制程度有高要求的商家。

服务器到服务器集成模式要求您符合 PCI 资格。根据业务需求提供以下材料完成验证:

有关 PCI DSS 合规要求的更多信息,请参阅 PCI DSS标准

用户体验

以下图示展示了首次支付和已保存的银行卡支付的用户体验。

首次支付

Web 用户体验

image.png

已保存的银行卡支付

Web 用户体验

image.png

支付流程

对于使用服务器到服务器模式的银行卡支付,支付流程包括以下步骤:

服务端-服务端模式-中.png

  1. 买家进入结账页面。
  2. 建支付请求。
    买家选择支付方式并提交订单后,调用 支付(收银台)接口以获取支付链接来完成支付。
  3. 处理支付推进链接。
    将支付推进链接返回给客户端。需要将买家重定向到支付推进链接。支付推进链接会继续支付流程,引导用户进行 3DS 认证流程。
  4. 获取支付结果。
    通过以下两种方法之一获取支付结果:
    • 异步通知:在 支付(收银台)接口中设置 paymentNotifyUrl 字段,以指定接收异步通知的地址。当支付成功或过期时, Antom 会使用 支付通知 向您发送异步通知。
    • 同步查询:调用 支付结果查询 接口来查询支付状态。
  1. 获取请款结果。 对于银行卡支付,您需要通过以下两种方法之一获取请款结果:

集成步骤

开始集成,请按照以下步骤操作:

  1. (可选)显示支付方式
  2. 授权支付
  3. 获取预授权支付结果
  4. 请款
  5. 获取请款结果

步骤 1:(可选)显示支付方式 客户端

添加您计划集成的支付方式的标识和名称。这使得买家可以轻松选择他们更喜欢的支付方式。您可以通过以下两种方式获取标识和名称:

  • 联系 Antom 技术支持。
  • 调用 咨询 接口,根据货币、交易发起终端类型、买家所在地区以及已签约的支付方式,获取当前交易支持的支付方式和标识链接。

添加支付方式后的页面效果如下所示:

image.png

步骤 2:授权支付 服务端

当买家选择由 Antom 提供的支付方式时,会显示银行卡信息收集页面给买家。在此过程中,您需要收集买家的银行卡信息、环境和订单信息。调用 支付(收银台)接口,为支付提交授权请求。

发起支付请求 服务端

Antom 提供了多种语言的服务器端接口库。以下代码以 Java 为例,需要安装 Java 6 或更高版本。

安装接口库

您可以在 GitHub 上找到最新版本。

copy
<dependency>
    <groupId>com.alipay.global.sdk</groupId>
    <artifactId>global-open-sdk-java</artifactId>
    <version>2.0.29</version>
</dependency>

初始化请求实例

copy
import com.alipay.global.api.AlipayClient;
import com.alipay.global.api.DefaultAlipayClient;

import com.alipay.global.api.AlipayClient;
import com.alipay.global.api.DefaultAlipayClient;
import com.alipay.global.api.model.constants.EndPointConstants;

public class Sample {
    public static final String        CLIENT_ID            = "";
    public static final String        ANTOM_PUBLIC_KEY     = "";
    public static final String        MERCHANT_PRIVATE_KEY = "";

    private final static AlipayClient CLIENT               = new DefaultAlipayClient(
            EndPointConstants.SG, MERCHANT_PRIVATE_KEY, ANTOM_PUBLIC_KEY, CLIENT_ID);

}

创建支付请求

在发送 支付(收银台)接口请求时,可以根据业务需求选择以下功能:

  • 3DS
    • 启用 3D 验证:在 支付(收银台)接口中设置 is3DSAuthentication 字段,以确定交易是否通过 3D Secure 进行验证。
    • 智能恢复交易:如果发卡行要求,将自动对非 3D Secure 交易发起 3D Secure 验证以恢复失败的交易。
    • MPI 能力:商户在交易发起前优先进行第三方验证,将验证结果信息传递到支付请求中,并指定非 3D 验证。如果商户未获取银行卡信息,则无法进行非 3D 验证。
  • 欺诈保护
    • 增值服务风险控制: Antom 在交易层面提供升级的风险控制服务,根据交易的风险等级智能决定风险控制策略。
    • 地址验证系统(AVS)服务:该服务验证提供的账单地址是否与发卡行记录中的持卡人地址匹配。AVS 有助于防止欺诈交易。
  • 令牌化
    • 存储银行卡信息:在首次支付时获取买家同意存储银行卡信息,以便下次支付时无需再次输入银行卡信息。
  • 分期付款
    • 分期付款:在支付时,买家可以选择分期付款方式,并按每个分期周期进行定期付款。买家完成首期分期付款后, Antom 会根据合同中指定的结算周期结算全额订单金额。
  • 特定功能
    • 商户发起交易(MIT):可在无需买家参与的情况下发起计划内或计划外的交易。
    • 指定银行卡品牌:当买家选择双标卡支付时,您可以指定银行卡品牌或授权支付的地区。

支付请求中包含以下参数。

参数名称

是否必需?

描述

productCode

在此场景中,值设置为 CASHIER_PAYMENT

paymentRequestId

商家为识别支付请求而分配的专属 ID。

paymentAmount

商家请求在订单货币中接收的支付金额。

paymentMethod

商家或收单机构用来收款的方法。

paymentRedirectUrl

支付完成后,买家被重定向到的商家页面链接。

settlementStrategy

支付请求的结算策略。

order

包括买家、商家、商品、金额、配送信息和购买环境的订单信息。

paymentMethodMetaData

银行卡信息:cardNo, cvv, expiryYear, expiryMonth, cardholderName, billingAddress

env

环境信息:用于提高支付成功率

有关完整参数的更多信息,请参阅 支付(收银台)接口。

调用支付接口的示例代码

以下示例代码用于发起支付:

copy
public static void payByCardServer2Server() {
    AlipayPayRequest alipayPayRequest = new AlipayPayRequest();
    alipayPayRequest.setProductCode(ProductCodeType.CASHIER_PAYMENT);

    // replace with your paymentRequestId
    String paymentRequestId = UUID.randomUUID().toString();
    alipayPayRequest.setPaymentRequestId(paymentRequestId);

    // set amount
    Amount amount = Amount.builder().currency("SGD").value("4200").build();
    alipayPayRequest.setPaymentAmount(amount);

    // set paymentMethod
    PaymentMethod paymentMethod = PaymentMethod.builder().paymentMethodType("CARD").build();
    alipayPayRequest.setPaymentMethod(paymentMethod);

    // card info
    Map<String, Object> paymentMethodMetaData = new HashMap<String, Object>();
    paymentMethodMetaData.put("cardNo", "4054695723100768");
    paymentMethodMetaData.put("cvv", "123");
    paymentMethodMetaData.put("expiryMonth", "12");
    paymentMethodMetaData.put("expiryYear", "99");
    paymentMethodMetaData.put("tokenize", false);

    JSONObject cardholderName = new JSONObject();
    cardholderName.put("firstName", "John");
    cardholderName.put("lastName", "Doe");
    paymentMethodMetaData.put("cardholderName", cardholderName);
    paymentMethod.setPaymentMethodMetaData(paymentMethodMetaData);

    // replace with your orderId
    String orderId = UUID.randomUUID().toString();

    // set buyer info
    Buyer buyer = Buyer.builder().referenceBuyerId("yourBuyerId").build();

    // set order info
    Order order = Order.builder().referenceOrderId(orderId)
        .orderDescription("antom testing order").orderAmount(amount).buyer(buyer).build();
    alipayPayRequest.setOrder(order);

    // set env info
    Env env = Env.builder().terminalType(TerminalType.WEB).clientIp("1.2.3.4").build();
    alipayPayRequest.setEnv(env);

    // set auth capture payment mode
    PaymentFactor paymentFactor = PaymentFactor.builder().isAuthorization(true).build();
    alipayPayRequest.setPaymentFactor(paymentFactor);

    // replace with your notify url
    alipayPayRequest.setPaymentNotifyUrl("https://www.yourNotifyUrl.com");

    // replace with your redirect url
    alipayPayRequest.setPaymentRedirectUrl("https://www.yourMerchantWeb.com");

    // do Payment
    AlipayPayResponse alipayPayResponse = null;
    try {
        alipayPayResponse = CLIENT.execute(alipayPayRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // handle error condition
    }
}

以下代码显示了请求报文的示例:

copy
{
  "env": {
    "clientIp": "1.2.3.4",
    "terminalType": "WEB"
  },
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "SGD",
      "value": "4200"
    },
    "orderDescription": "antom testing order",
    "referenceOrderId": "referenceOrderId01"
  },
  "paymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "paymentFactor": {
    "isAuthorization": true
  },
  "paymentMethod": {
    "paymentMethodMetaData": {
      "cvv": "123",
      "cardholderName": {
        "firstName": "John",
        "lastName": "Doe"
      },
      "expiryMonth": "12",
      "expiryYear": "99",
      "cardNo": "4054695723100768",
      "tokenize": false
    },
    "paymentMethodType": "CARD"
  },
  "paymentNotifyUrl": "https://www.yourNotifyUrl.com",
  "paymentRedirectUrl": "https://www.yourMerchantWeb.com",
  "paymentRequestId": "paymentRequestId01",
  "productCode": "CASHIER_PAYMENT"
}

常见问题

问:如何设置 terminalType

答:terminalType 的有效值为:

  • 如果买家在PC端发起交易,需要将 terminalType 指定为WEB
  • 如果买家在移动浏览器上发起交易,需要将 terminalType 指定为WAP。添加 osType 参数,并根据买家的手机填写相应的系统参数ANDROIDIOS
  • 如果买家在应用内发起交易,需要将 terminalType 指定为APP

问:请求参数的值可以使用中文字符吗?

答:为了避免特定支付方式的兼容性问题,请求中的字段请勿使用中文字符。

问: 如何设置接收支付通知的链接?

答:在 支付会话创建(收银台)接口中指定 paymentNotifyUrl 字段,以接收支付结果的异步通知(支付通知),或者在 Antom Dashboard 中配置接收链接。如果请求和 Antom Dashboard 中都指定了链接,请求中的值优先。

接收支付响应

返回参数包含以下内容:

  • normalUrl: 跳转链接地址。
  • paymentId: 订单 ID。

以下是示例响应:

授权成功
copy
{
  "paymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "paymentCreateTime": "2024-01-01T00:00:00+08:00",
  "paymentId": "20240101123456789XXXX",
  "paymentRequestId": "paymentRequestId01",
  "paymentResultInfo": {
    "avsResultRaw": "A",
    "cardBrand": "VISA",
    "cardNo": "****************",
    "cvvResultRaw": "Y",
    "networkTransactionId": "networkTransIdXXXX",
    "threeDSResult": {
      "cavv": "",
      "eci": ""
    }
  },
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

常见问题

问:什么是 normalUrl

答:对于 Web 或 WAP 交易,Antom 会返回 normalUrl,服务器端需要将其传递给客户端进行重定向。当您再次为同一订单发起支付时,需要获取新的 normalUrl 进行重定向。

问:什么是 paymentId

答:如果您需要存储相应的订单 ID 以备后续退款和对账,可以指定 paymentId

跳转到支付推进链接 客户端

商户服务器将 normalUrl 传递给前端,前端页面处理重定向到 normalUrl 的过程。

步骤 3:获取预授权支付结果 服务端

当买家完成支付或支付超时,您可以通过 Antom 异步通知或主动查询支付结果来获取相应的支付结果。

从异步通知和 支付结果查询 获取的响应包含授权支付结果以及其它关键信息,例如:

接口

授权结果

AVS 信息

CVV 信息

3DS 身份验证信息

支付通知

resultStatus

avsResultRaw

cvvResultRaw

threeDSResult(仅 3DS 身份验证授权时可用)

支付结果查询

paymentStatus

paymentStatus

cvvResultRaw

threeDSResult(仅 3DS 认证授权时可用)

接收异步通知

当支付成功或失败时,Antom 会向您在 支付(收银台)接口的 paymentNotifyUrl 参数中指定的地址发送异步通知(支付通知)。如果每个支付的地址相同,您也可以在 Antom Dashboard 中配置该地址。

以下是一个 3D 支付通知的示例代码:

copy
{
  "actualPaymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "cardInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "networkTransactionId": "XXXXX",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "cavv": "",
      "eci": ""
    }
  },
  "notifyType": "PAYMENT_RESULT",
  "paymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "paymentCreateTime": "2024-01-01T00:00:00+08:00",
  "paymentId": "20240101123456789XXXX",
  "paymentRequestId": "paymentRequestId01",
  "paymentResultInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "eci": "02",
      "threeDSVersion": "2.2.0",
      "caav": "cavvSample"
    }
  },
  "paymentTime": "2024-01-01T00:01:00+08:00",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

以下是一个非 3D 支付通知的示例代码:

copy
{
  "actualPaymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "cardInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "networkTransactionId": "XXXXX",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "cavv": "",
      "eci": ""
    }
  },
  "notifyType": "PAYMENT_RESULT",
  "paymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "paymentCreateTime": "2024-01-01T00:00:00+08:00",
  "paymentId": "20240101123456789XXXX",
  "paymentRequestId": "paymentRequestId01",
  "paymentResultInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "networkTransactionId": "XXXXX",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "cavv": "",
      "eci": ""
    }
  },
  "paymentTime": "2024-01-01T00:01:00+08:00",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

如何验证通知的签名并做出响应,请参阅签名与验签

常见问题

问:何时会发送通知?

答:这取决于支付是否完成:

  • 如果支付成功完成,Antom 通常会在 3 到 5 秒内发送异步通知。对于某些支付方式,如柜台支付(OTC),通知可能会稍有延迟。
  • 如果支付未完成,Antom 需要先关闭订单,然后发送异步通知。不同支付方式关闭订单所需的时间会有所不同,通常默认为 14 分钟。

问:异步通知会被重新发送吗?

答:是的,对于以下情况,异步通知会在 24 小时内自动重新发送:

  • 如果由于网络原因未收到异步通知。
  • 如果您收到来自 Antom 的异步通知,但您没有按照处理通知的示例代码格式对通知做出响应。

通知最多可以重发 8 次,或者直到收到正确的响应以终止发送。发送间隔如下:0 分钟,2 分钟,10 分钟,10 分钟,1 小时,2 小时,6 小时,15 小时。

问:在响应异步通知时,我需要添加数字签名吗?

答:如果您收到来自 Antom 的异步通知,您需要按照处理通知的示例代码格式返回响应,但不需要对响应进行签名。

问:我在通知中需要使用哪些关键参数?

答:请注意以下关键参数:

  • result :表示订单的支付结果。
  • paymentRequestId :用于咨询、取消和对账的支付请求 ID。
  • paymentId :表示由 Antom 生成的支付订单 ID,用于退款和对账。
  • paymentAmount :表示支付金额。

查询支付结果

发起查询请求涉及以下参数。

参数名称

是否必需?

描述

paymentRequestId

商户生成的支付请求 ID

以下是调用 支付结果查询 接口的示例代码:

copy
public static void inquiryPayment() {
    AlipayPayQueryRequest alipayPayQueryRequest = new AlipayPayQueryRequest();

    // replace with your paymentRequestId
    alipayPayQueryRequest.setPaymentRequestId("yourPaymentRequestId");

    AlipayPayQueryResponse alipayPayQueryResponse = null;
    try {
        alipayPayQueryResponse = CLIENT.execute(alipayPayQueryRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // handle error condition
    }
}

以下代码展示了请求报文的示例:

copy
{
  "paymentRequestId": "paymentRequestId01"
}

以下是 3D 支付响应消息的示例:

copy
{
  "authExpiryTime": "2024-01-08T00:01:00+08:00",
  "cardInfo": {
    "cardBrand": "MASTERCARD",
    "funding": "DEBIT",
    "issuingCountry": "US"
  },
  "paymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "paymentId": "20240101123456789XXXX",
  "paymentMethodType": "CARD",
  "paymentRedirectUrl": "https://www.yourMerchantWeb.com",
  "paymentRequestId": "paymentRequestId01",
  "paymentResultCode": "SUCCESS",
  "paymentResultInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "networkTransactionId": "networkTransIdXXXX",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "eci": "02",
      "threeDSVersion": "2.2.0",
      "caav": "cavvSample"
    }
  },
  "paymentResultMessage": "success",
  "paymentStatus": "SUCCESS",
  "paymentTime": "2024-01-01T00:01:00+08:00",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

以下是非 3D 支付响应消息的示例:

copy
{
  "authExpiryTime": "2024-01-08T00:01:00+08:00",
  "cardInfo": {
    "cardBrand": "MASTERCARD",
    "funding": "DEBIT",
    "issuingCountry": "US"
  },
  "paymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "paymentId": "20240101123456789XXXX",
  "paymentMethodType": "CARD",
  "paymentRedirectUrl": "https://www.yourMerchantWeb.com",
  "paymentRequestId": "paymentRequestId01",
  "paymentResultCode": "SUCCESS",
  "paymentResultInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "networkTransactionId": "networkTransIdXXXX",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "cavv": "",
      "eci": ""
    }
  },
  "paymentResultMessage": "success",
  "paymentStatus": "SUCCESS",
  "paymentTime": "2024-01-01T00:01:00+08:00",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

常见问题

问:我应该多久调用一次 支付结果查询 接口?

答:以 2 秒的间隔持续调用 支付结果查询 接口,直到获取最终的支付结果或收到异步支付结果通知为止。

问:我在通知中需要使用哪些关键参数?

答:请注意以下关键参数:

  • result :表示此 支付结果查询 接口调用的结果,需要根据 paymentStatus 来判断订单状态:
    • SUCCESSFAIL表示最终结果。
    • PROCESSING表示处理中。
  • paymentAmount :表示支付的金额。

步骤 4:请款 服务端

默认情况下,Antom会自动为您处理资金请款。如果您在调用 支付(收银台)接口时将 paymentFactor.captureMode 的值设置为MANUAL,则必须通过调用 请款(收银台)接口来手动进行资金请款。

  • 自动请款Antom 会在买家授权支付后立即自动进行资金请款。请款结果会通过异步通知传达给您。当满足以下任一条件时,自动请款将被激活:
    • 您在 支付(收银台)接口 中将 paymentFactor.captureMode 的值设置为 AUTOMATIC
    • 您保留了 paymentFactor.captureMode 支付(收银台)接口中的值为空,或者没有传递此参数。
  • 手动请款:成功授权后七天内,您必须通过调用 请款(收银台)接口来发起资金请款,否则 Antom 将自动解冻支付的授权资金。请款总额必须等于授权总额。

注意:对于银行卡支付,建议在发货前确认成功的请款结果。

请款(收银台)接口中的主要请求参数如下表所示:

参数

描述

captureRequestId

您为请款请求分配的专属 ID。

paymentId

Antom为订单分配的专属 ID。

captureAmount

请款金额,必须等于授权的总金额。

以下是发起支付的示例代码和数据包:

copy
public static void capture() {
    AlipayCaptureRequest alipayCaptureRequest = new AlipayCaptureRequest();

    Amount amount = Amount.builder().currency("SGD").value("4200").build();
    alipayCaptureRequest.setCaptureAmount(amount);

    // replace with your captureRequestId
    String captureRequestId = UUID.randomUUID().toString();
    alipayCaptureRequest.setCaptureRequestId(captureRequestId);

    // replace with your paymentId
    alipayCaptureRequest.setPaymentId("20240101123456789XXXX");

    AlipayCaptureResponse alipayCaptureResponse = null;
    try {
        alipayCaptureResponse = CLIENT.execute(alipayCaptureRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
    }
}

以下代码显示了请求报文的示例:

copy
{
  "captureAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "captureRequestId": "captureRequestId01",
  "paymentId": "20240101123456789XXXX"
}

步骤 5:获取请款结果 服务端

当买家完成请款或请款超时,您可以通过 Antom 异步通知或主动查询支付结果来获取相应的支付结果。

接收异步通知

完成支付或支付失败时,Antom 会通过 请款通知(收银台)发送异步通知。您可以在 支付(收银台)接口中通过 paymentNotifyUrl 参数指定。如果每个支付的地址相同,您也可以在 Antom Dashboard 中进行配置。

以下是通知请求的示例代码:

copy
{
  "captureAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "notifyType": "CAPTURE_RESULT",
  "captureId": "20240101987654321XXXX",
  "captureRequestId": "captureRequestId01",
  "captureTime": "2024-01-01T00:00:02+08:00",
  "paymentId": "20240101123456789XXXX",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

以下是支付失败的通知示例:

copy
{
  "captureAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "notifyType": "CAPTURE_RESULT",
  "captureId": "20240101123456789XXXX",
  "captureRequestId": "captureRequestId01",
  "captureTime": "2024-01-01T00:00:02+08:00",
  "paymentId": "20240101123456789XXXX",
  "result": {
    "resultCode": "PROCESS_FAIL",
    "resultMessage": "fail.",
    "resultStatus": "F"
  }
}

如何验证通知的签名并作出响应,参见签名与验签

常见问题

问:异步通知会被重新发送吗?

答:是的,对于以下情况,异步通知将在 24 小时内自动重新发送:

  • 如果您因网络原因未收到异步通知。
  • 如果您收到来自 Antom 的异步通知,但您没有按照处理通知的示例代码格式进行响应。

通知最多可以重发 8 次,或者直到收到正确的响应以终止发送。发送间隔为:0 分钟,2 分钟,10 分钟,10 分钟,1 小时,2 小时,6 小时和 15 小时。

问:在响应异步通知时,我需要添加数字签名吗?

答:如果您收到来自 Antom 的异步通知,您需要按照处理通知的示例代码格式返回响应,但不需要对响应进行签名。

问:我需要使用通知中的哪些关键参数?

答:请注意以下关键参数:

  • result :表示订单的请款结果。
  • notifyType :通知类型为 CAPTURE_RESULT
  • paymentRequestId :您生成的支付请求 ID,用于查询、取消和对账。
  • paymentIdAntom 生成的支付订单 ID,用于退款和对账。
  • acquirerReferenceNo :集成新加坡和香港内银行卡支付服务的商户将在通知中收到特定的收单机构 ID。

查询请款结果

发起查询请求会涉及以下参数。

参数名称

是否必需?

描述

paymentRequestId

由商户生成的支付请求 ID

以下是示例代码:

copy
public static void inquiryPayment() {
    AlipayPayQueryRequest alipayPayQueryRequest = new AlipayPayQueryRequest();

    // replace with your paymentRequestId
    alipayPayQueryRequest.setPaymentRequestId("yourPaymentRequestId");

    AlipayPayQueryResponse alipayPayQueryResponse = null;
    try {
        alipayPayQueryResponse = CLIENT.execute(alipayPayQueryRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // handle error condition
    }
}

以下是请求报文:

copy
{
  "paymentRequestId": "paymentRequestId01"
}

请款状态值

接口响应中 transactions 字段的值表示请款状态:

参数

描述

transactions.transactionType

值为CAPTURE,表示请款状态。

transactions.transactionResult

请款结果。

transactions 的值表示请款状态。详细信息请参阅以下代码示例:

copy
{
  "transactions": [
    {
      "transactionType": "CAPTURE",
      "transactionStatus": "SUCCESS",
      "transactionRequestId": "captureRequestId01",
      "transactionAmount": {
        "currency": "SGD",
        "value": "4200"
      },
      "transactionTime": "2024-01-01T00:00:02+08:00",
      "transactionId": "20240101123456789XXXX",
      "transactionResult": {
        "resultStatus": "S",
        "resultCode": "SUCCESS",
        "resultMessage": "success"
      }
    }
  ]
}

transactions 的值表示请款状态 。请查看以下请款失败的代码示例:

copy
{
  "transactions": [
    {
      "transactionType": "CAPTURE",
      "transactionStatus": "SUCCESS",
      "transactionRequestId": "captureRequestId01",
      "transactionAmount": {
        "currency": "SGD",
        "value": "4200"
      },
      "transactionTime": "2024-01-01T00:00:02+08:00",
      "transactionId": "20240101123456789XXXX",
      "transactionResult": {
        "resultStatus": "F",
        "resultCode": "PROCESS_FAIL",
        "resultMessage": "General business failure. No retry."
      }
    }
  ]
}

transactions 的值表示请款状态 。请查看以下处理中的请款代码示例:

copy
{
  "transactions": [
    {
      "transactionType": "CAPTURE",
      "transactionStatus": "SUCCESS",
      "transactionRequestId": "captureRequestId01",
      "transactionAmount": {
        "currency": "SGD",
        "value": "4200"
      },
      "transactionTime": "2024-01-01T00:00:02+08:00",
      "transactionId": "20240101123456789XXXX",
      "transactionResult": {
        "resultStatus": "F",
        "resultCode": "PROCESS_FAIL",
        "resultMessage": "General business failure. No retry."
      }
    }
  ]
}

常见问题

问:我应该多久调用一次 支付结果查询 接口?

答:以 2 秒的间隔持续调用 支付结果查询 接口,直到获取最终的支付结果或收到异步支付结果通知。

最佳实践

遵循以下最佳实践以提高集成效率。

自定义支付超时时间

在收银台支付场景中,Antom 端的默认超时时间为 14 分钟。超时后,买家无法继续支付。 您可以通过 支付(收银台)接口中的 paymentExpireTime 参数指定超时时间。 超过指定时间后,买家将无法扫码或登录结账页面。

以下示例代码展示了如何在 支付(收银台)接口中指定 paymentExpireTime 参数:

copy
{
  "env": {
    "clientIp": "1.2.3.4",
    "terminalType": "WEB"
  },
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "SGD",
      "value": "4200"
    },
    "orderDescription": "antom testing order",
    "referenceOrderId": "referenceOrderId01"
  },
  "paymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "paymentExpiryTime": "2024-01-01T00:30:00+08:00",
  "paymentFactor": {
    "isAuthorization": true
  },
  "paymentMethod": {
    "paymentMethodMetaData": {
      "cvv": "123",
      "cardholderName": {
        "firstName": "John",
        "lastName": "Doe"
      },
      "expiryMonth": "12",
      "expiryYear": "99",
      "cardNo": "4054695723100768",
      "tokenize": false
    },
    "paymentMethodType": "CARD"
  },
  "paymentNotifyUrl": "https://www.yourNotifyUrl.com",
  "paymentRedirectUrl": "https://www.yourMerchantWeb.com",
  "paymentRequestId": "paymentRequestId01",
  "productCode": "CASHIER_PAYMENT"
}

建议将接口超时时间设置为 10 秒,以提高响应成功率。

商户结果页面处理逻辑建议

  1. 处理重定向问题

当买家成功完成支付,但在跳转到您指定的 paymentRedirectUrl 时遇到问题,无论是由于网络问题还是支付方式的限制,请注意以下两点:

    • 不能将客户端重定向作为判断支付成功的依据。
    • 如果支付方法页面的 paymentRedirectUrl 未能重定向到商户页面,买家可以手动点击原始商户页面。为了避免买家误以为订单未支付而再次尝试支付,建议在原始商户页面上实现一个弹出窗口,用于查询交易结果。当买家点击此弹出窗口时,应显示交易结果,防止重复支付尝试。
  1. 重定向后触发订单结果查询

如果在调用 支付结果查询 接口后,商户端弹出,建议处理以下不同的结果:

    • 支付成功:支付成功后,页面将显示与发货相关的内容。
    • 支付失败:表明支付失败,并提供重试支付的指导,以帮助订单完成。
    • 支付处理中:显示加载效果,并在 3-5 秒内暂停,然后再次查询服务器以获取支付结果。如果结果仍然不确定(既不是成功也不是失败),建议显示“订单处理中”或“通过订单管理门户查看最终结果”。避免将延迟归因于“网络处理”。

支付失败重试

当订单的支付尝试失败,且买家可以为同一订单重试支付时,我们建议遵循以下集成流程以实现无缝体验:

  1. 在支付请求中,将 referenceOrderId 设置为订单 ID,将 paymentRequestId 设置为支付订单 ID。
  2. 如果需要对同一订单重试支付,首先检查订单状态。如果支付已经成功,向买家显示“已完成支付”。如果没有,再次调用 支付 接口以获取新的 normalUrl 进行重定向。虽然 referenceOrderId 保持不变,因为它代表相同的订单,但后续支付尝试时必须更新 paymentRequestId
  3. 确保每个商户订单只与一个成功的支付相关联。如果检测到单个订单有多个成功的支付,调用 取消支付 接口来为买家发起退款。
  4. 对于不支持退款的支付方式,建议在发起新支付之前取消原始支付。

获取支付结果

为了确保稳定地获取支付结果,避免出现买家完成支付但您未收到支付结果的情况,建议您在以下阶段检查支付结果:

  1. 当显示商户支付结果页面时。
  2. 在发货给买家之前。
  3. 当您收到 Antom 对账文件时。