绑卡(端到端 API )

本文指导您如何通过服务端到服务端模式 API 集成独立绑卡,以允许买家在支付过程的任何阶段绑定银行卡。在此方案中,您可以自己采集买家的卡明文信息并交给 Antom 进行存储,绑定成功后 Antom 会返回对应的卡令牌(cardToken)。在后续的交易中,您可以直接使用 cardToken 参数来发起支付,而无需再次收集买家的卡信息。关于令牌(cardToken)支付,请查看卡信息存档交易(COF)

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

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

用户体验

以下图展示了绑卡操作以及绑卡成功后后续支付的用户体验。

绑卡操作

下图展示了不同端的绑卡操作流程:

Card binding web user experience of API integration.png

后续支付

下图展示了不同端绑卡成功后后续支付的用户体验:

1764136825285-27ff95b3-62bb-41f5-9172-86408088dffd.png

绑卡流程

端到端模式下的绑卡流程包括以下步骤:

端到端绑卡时序图.png

  1. 买家点击绑卡按钮。
  2. 调用 vaultPaymentMethod 接口,发起绑卡请求。
    买家点击绑卡按钮后,您的服务端向 Antom 服务端调用 vaultPaymentMethod 接口来发起绑卡请求。
  3. 买家完成绑卡。
    买家在商户页面完成绑卡。
  4. (可选)买家完成 3D 验证。
    如果您指定 3D 验证,Antom 将会返回 3D URL 给客户端。您需要将买家重定向到 3D 验证页面(normalUrl),引导买家进行 3D 验证流程。
  5. 获取绑卡结果。
    您可以通过以下两种方法之一获取绑卡结果:
    • 异步通知:Antom 会使用 notifyVaulting 接口向您发送绑卡结果通知。
    • 同步查询:调用 inquireVaulting 接口来查询绑卡状态。

注意:您需要在绑卡管理页面向买家呈现已绑卡的脱敏卡号。在后续支付时,推荐您在请求中传入首次绑卡时通过 notifyVaulting 或者 inquireVaulting 获取的 cardToken,也可以传入存储的明文卡号。

集成准备

在您开始集成前,请阅读集成指南接口概述文档,了解服务端接口的集成步骤及调用接口的注意事项,并确保已完成以下预配置:

  • 已获得 client ID。
  • 已完成密钥配置。
  • 已完成异步通知接收地址的配置。
  • 集成服务端 SDK 资源包,并完成接口库安装及请求示例初始化。具体操作请参阅服务端 SDK

集成步骤

大多数银行卡支付遵循 Antom 提供的通用支付流程和集成过程。请按照以下步骤开始集成:

  1. 发起绑卡请求
  2. (可选)跳转至 3D 验证页面(normalUrl
  3. 获取绑卡结果

步骤 1:发起绑卡请求 服务端

调用 vaultPaymentMethod 接口,并传入以下重点请求字段:

参数名称

是否必需

描述

paymentMethodDetail.card

传入需要绑定的银行卡信息。

vaultingRequestId

由商户生成的专属 ID。每次发起绑卡操作时,必须有新的 ID。

paymentMethodDetail.paymentMethodType

该字段设置为 CARD

paymentMethodDetail.card.is3DSAuthentication

设置该笔绑卡请求是否需要 3D 验证。若需要开启 3D 验证,请指定 true

注意

  • 韩国本地卡不支持 3D 验证。
  • 若后续支付有 MIT 场景,则卡组强烈建议绑卡指定为 3D。

redirectUrl

商户端绑定的结果页面,根据服务器端的结果来显示。

vaultingNotificationUrl

绑卡结果通知地址。

env

买家发起绑卡请求的环境。

有关完整参数的更多信息,请参阅 vaultPaymentMethod 接口。

您需要调用 vaultPaymentMethod 接口发起绑卡请求。以下示例代码展示了如何调用 vaultPaymentMethod 接口

copy
public static void  vaultPaymentMethod(){
    AlipayVaultingPaymentMethodRequest alipayVaultingPaymentMethodRequest = new AlipayVaultingPaymentMethodRequest();

    // 设置卡信息
    CardPaymentMethodDetail cardPaymentMethodDetail = CardPaymentMethodDetail.builder().cardNo("4054695723100768").expiryMonth("01").expiryYear("2030").build();
    PaymentMethodDetail paymentMethodDetail = PaymentMethodDetail.builder().paymentMethodType("CARD").card(cardPaymentMethodDetail).build();
    alipayVaultingPaymentMethodRequest.setPaymentMethodDetail(paymentMethodDetail);

    // 设置环境
    Env env = Env.builder().terminalType(TerminalType.WEB).build();
    alipayVaultingPaymentMethodRequest.setEnv(env);

    // 替换为您的 vaultingRequestId
    String vaultingRequestId = UUID.randomUUID().toString();
    alipayVaultingPaymentMethodRequest.setVaultingRequestId(vaultingRequestId);

    // 替换为您的 notificationUrl
    alipayVaultingPaymentMethodRequest.setVaultingNotificationUrl("https://www.yourNotifyUrl.com");

    // 替换为您的 redirectUrl
    alipayVaultingPaymentMethodRequest.setRedirectUrl("https://www.yourMerchantWeb.com");

    // 绑卡
    AlipayVaultingPaymentMethodResponse alipayVaultingPaymentMethodResponse;

    try{
        alipayVaultingPaymentMethodResponse = CLIENT.execute(alipayVaultingPaymentMethodRequest);
    }catch (AlipayApiException e){
        String errorMsg = e.getMessage();
    }
}

以下代码展示了发起绑卡请求报文的示例,同时您可以指定是否需要进行 3D 验证:

copy
{
  "vaultingRequestId": "VAULT_20250508183612361_AUTO",
  "vaultingNotificationUrl": "https://kademo.intlalipay.cn/payments/notifySuccess",
  "redirectUrl": "https://kademo.intlalipay.cn/melitigo/Test_114.html",
  "paymentMethodDetail": {
    "paymentMethodType": "CARD",
    "card": {
      "cvv": "**5",
      "cardholderName": {
        "firstName": "liang",
        "lastName": "x*****n"
      },
      "expiryMonth": "**",
      "expiryYear": "**",
      "payerEmail": "2421********com",
      "cardNo": "537********1310"
    }
  },
  "env": {
    "terminalType": "APP",
    "osType":"IOS",
    "clientIp":"12.99.168.1"
  }
}

以下是部分响应报文示例:

copy
{
  "paymentMethodDetail": {
    "card": {
      "brand": "MASTERCARD",
      "cardToken": "ALIPAYEfG2DFbGx2Eh****************************XA7nyWCloE4MwfmN48sP1+rSPQ==",
      "maskedCardNo": "************1310"
    },
    "paymentMethodType": "CARD"
  },
  "vaultingRequestId": "VAULT_20250508183612361_AUTO",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

下表展示了响应报文中 result.resultStatus 字段可能返回的值,请您根据指引进行处理:

result.resultStatus

信息

下一步操作

S

表示绑卡成功。

建议储存以下参数用作后续支付:

  • cardToken由 Antom 生成的令牌,用于后续支付。您可以将该信息和您的买家做绑定,用于后续支付。
  • brand:卡品牌。建议您存储该信息用于后续支付时展示给买家。
  • maskedCardNo:脱敏的银行卡号。建议您存储该信息用于后续支付时展示给买家。

F

表示绑卡失败。

请检查并验证当前接口所需的请求字段(包括头部字段和正文字段)是否正确传递并有效。

U

表示绑卡进行中。

根据 resultCode 的值是否为 VERIFICATION_IN_PROCESS 进行操作:

  • 结果代码不是 VERIFICATION_IN_PROCESS:表示接口调用失败,请使用新 vaultingRequestId 再次调用此接口。
  • 结果代码是 VERIFICATION_IN_PROCESS:请检查是否返回了normalUrl
    • 如果返回了 normalUrl: 表示绑卡创建成功。将买家跳转到提供的特定链接以完成绑卡。
    • 如果没有返回 normalUrl: 表示绑卡创建失败。请使用新的 vaultingRequestId 值再次调用此接口。如果问题持续存在,请联系 Antom 技术支持。

注意如果您未收到响应报文,可能是网络超时所致,请您保持 vaultingRequestId 不变调用接口重试

步骤 2:(可选)跳转至 3D 验证页面(normalUrl

商户服务端获取 Antom 返回的 3D 验证链接(normalUrl 后,将该 normalUrl 传递给前端,由商户前端跳转至 3D 验证页面。当绑卡完成后,会回跳到您在绑卡请求里传入的 redirectUrl,您需要根据 Antom 返回的绑卡结果进行展示。

copy
if (serverResponse.normalUrl != null) {
  window.open(serverResponse.normalUrl, '_blank');
}

常见问题

问:是否支持 3D 功能?

答:支持,详情请查看 3D Secure 2

步骤 3:获取绑卡结果 服务端

您可以通过以下两种方法之一获取绑卡结果:

  • 异步通知:Antom 会使用 notifyVaulting 接口向您发送绑卡结果通知。
  • 查询绑卡结果:调用 inquireVaulting 接口来查询绑卡状态。

异步通知

1. 设置接收通知的 webhook URL

若您的每次绑卡操作都有单独的通知 URL,建议您在每笔请求中设置 webhook URL。您可以通过 vaultPaymentMethod 接口请求的 vaultingNotificationUrl 字段传入该次绑卡结果的接收异步通知 URL。

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

copy
{
    "paymentMethodDetail": {
        "card": {
            "brand": "MASTERCARD",
            "cardToken": "ALIPAY34RlcCU3ZtZ***********************sVYl8x244tyWCloE4MwfmN48sP1+rSPQ==",
            "maskedCardNo": "************1310",
            "networkTransactionId": "112000********575887"
        },
        "paymentMethodType": "CARD"
    },
    "vaultingRequestId": "VAULT_20250*****3348834_AUTO",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

您可能会收到请求报文中 result.resultStatus 字段的不同值,请您根据下表指引进行处理:

result.resultStatus

信息下一步操作

S

表示绑卡成功。

建议您保存以下关键参数信息用于后续支付:

  • cardToken :由 Antom 生成的令牌,用于后续支付。您可以将该信息和您的买家做绑定,用于后续支付。
  • brand:卡品牌。建议您存储该信息用于后续支付时展示给买家。
  • maskedCardNo:脱敏的银行卡号。建议您存储该信息用于后续支付时展示给买家。

F

表示绑卡失败。

请您根据 result.resultCode 查看具体失败原因。

2. 异步通知验签

若您收到 Antom 的异步通知,需要您在返回中按照示例代码格式返回响应,但无需做加签处理。

您需要对 Antom 发送的绑卡结果通知进行验签。

copy
import javax.servlet.http.HttpServletRequest;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.alipay.global.api.model.Result;
import com.alipay.global.api.model.ResultStatusType;
import com.alipay.global.api.response.AlipayResponse;
import com.alipay.global.api.tools.WebhookTool;

@RestController
public class PaymentNotifyHandleBySDK {

    /**
     * alipay public key, used to verify signature
     */
    private static final String SERVER_PUBLIC_KEY = "";
            
    /**
     * payment result notify processor
     * using <a href="https://spring.io">Spring Framework</a>
     *
     * @param request    HttpServletRequest
     * @param notifyBody notify body
     * @return
     */
    @PostMapping("/payNotify")
    public Object payNotifyHandler(HttpServletRequest request, @RequestBody String notifyBody) {

        // 从 http 请求中获取所需参数
        String requestUri = request.getRequestURI();
        String requestMethod = request.getMethod();

        // 从请求头中获取所需参数
        String requestTime = request.getHeader("request-time");
        String clientId = request.getHeader("client-id");
        String signature = request.getHeader("signature");

        Result result;
        AlipayResponse response = new AlipayResponse();

        try {
            // 通知验签
            boolean verifyResult = WebhookTool.checkSignature(requestUri, requestMethod, clientId, requestTime, signature, notifyBody, SERVER_PUBLIC_KEY);
            if (!verifyResult) {
                throw new RuntimeException("Invalid notify signature");
            }

            // 反序列化通知主体
            
            // 根据通知结果更新订单状态

            // 响应服务端已接收通知
            result = new Result("SUCCESS", "success", ResultStatusType.S);

        } catch (Exception e) {
            String errorMsg = e.getMessage();
            // 处理错误情况
            result = new Result("ERROR", errorMsg, ResultStatusType.F);
        }
        response.setResult(result);
        return ResponseEntity.ok().body(response);
    }

}

您无需对响应通知结果做加签处理,但是对于每个通知请求均需按以下固定格式响应,与绑卡成功与否无关。

copy
{
    "result": {
        "resultCode": "SUCCESS",
        "resultStatus": "S",
        "resultMessage": "success"
    }
}

常见问题

问:绑卡成功后会立即发送异步通知?

答:绑卡结果的异步通知会秒级发送,一般会在绑卡成功后 3-5 秒发送。

问:绑卡失败后会发送异步通知吗?

答:会发送。

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

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

  • 如果由于网络原因未收到异步通知。
  • 如果您收到来自 Antom 的异步通知,但您没有按照处理通知的示例代码格式对通知做出响应。
通知最多可以重发 8 次,或者直到收到正确的响应以终止发送。发送间隔如下:0 分钟、2 分钟、10 分钟、10 分钟、1 小时、2 小时、6 小时和15 小时。

问:收到绑卡结果通知是否需要验签?

答:需要通过验签 Antom 会发送保障回调请求给您,验签时请注意拼装待验签报文时需按标准处理:<http-method> <http-uri> <client-id>.<request-time>.<request-body>,特别是针对 <request-body> 需直接取值而非解析 JSON 后拼装。

问:返回的 cardToken 有效期是多久?

答:cardToken 本身永久有效,若卡片到期,则会同时失效。若卡片到期,您再次调用支付请求,Antom 会返回 INVALID_EXPIRATION_DATE 错误码,建议您引导买家重新绑定更新后的卡信息。

:是否一定要使用 Antom 返回的 cardToken 用于后续支付?

答:若您在绑卡完成后,自己会存储明文的卡号,您可以使用明文卡号进行后续支付。但强烈建议您使用 Antom 的 cardToken 发起后续交易,因为数据显示使用 Antom 的 cardToken 发起后续 COF 交易的成功率明显高于您使用自行存储卡信息的方式。

问:是不是所有 3DS 交易都会返回 normalUrl 用于跳转到 3DS 链接?

答:不是。在部分场景下,发卡行会同步返回 3DS 无摩擦通过的结果,那么 Antom 就会在 vaultPaymentMethod 接口的同步响应的报文中,直接返回授权结果 (resultStatus) 和 3DS 结果 (threeDSResult)。