令牌支付

本文介绍了如何通过桌面网页浏览器实现自动扣款功能的集成方案。集成后,您的网站即可上线自动扣款服务。买家首次授权后,后续支付无需重复输入密码,将直接从网站完成扣款操作,适合需要快速扣款的商业场景。

用户体验

以下图片展示了买家进行授权与支付的体验流程:

授权

买家跳转到 Antom 页面,通过输入账号和密码完成授权。目前仅支持从商户 web 端发起授权。

image.png

支付流程

支付流程由以下集成步骤组成:

yuque_diagram (19).png

  1. 买家进入结账页面
  2. 获取签约授权链接
    • 买家选择支付方式并提交订单后,商户端调用 consult 接口以获取授权链接。
  1. 获取授权结果
    • 商户可通过支付方式返回的重构 URL 或者异步通知获取授权结果。
  1. 申请支付令牌
    • 调用 applyToken 接口来申请支付令牌,获取到对应令牌后存储到本地。
  1. 发起支付并获取支付结果
    • 在获得买家授权后,商户端直接调用 pay(令牌支付)接口发起代扣服务,并通过同步响应、支付查询或者支付 notifyPayment 获取支付结果。

集成准备

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

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

集成步骤

要获得买家授权并进行代扣,请完成以下集成步骤:

  1. 获取并跳转到授权链接
  2. 获取授权结果
  3. 申请支付令牌
  4. 发起代扣
  5. 获取支付结果

步骤 1: 获取并跳转到授权链接

1. 调用咨询接口 服务端

consult 请求中传入以下参数:

参数名称

是否必需

描述

customerBelongsTo

买家使用的钱包。在 Antom 全球商家账户中,该参数的值固定为 ANTOM_BIZ_ACCOUNT

authRedirectUrl

授权完成后,用于将买家跳转到商户页面的链接。

terminalType

指商户端所在的终端类型。此场景下,字段值为 WEB

目前仅支持 PC。

authState

识别授权请求而传入的字符串。

scopes

在此场景中,该字段设置为 AGREEMENT_PAY

以下代码为调用 consult 接口以获取授权链接的示例:

copy
public static void authorizationConsult() {
    AlipayAuthConsultRequest alipayAuthConsultRequest = new AlipayAuthConsultRequest();

    // 替换为您的 authState
    String authState = UUID.randomUUID().toString();
    alipayAuthConsultRequest.setAuthState(authState);

    // 设置请求授权的目标支付方式
    alipayAuthConsultRequest.setCustomerBelongsTo(CustomerBelongsTo.TOSSPAY);

    // 设置授权范围
    alipayAuthConsultRequest.setScopes(new ScopeType[]{ScopeType.AGREEMENT_PAY});

    // 设置 terminalType
    alipayAuthConsultRequest.setTerminalType(TerminalType.WEB);

    // 替换为您的 authRedirectUrl
    alipayAuthConsultRequest.setAuthRedirectUrl("http://www.yourRedirectUrl.com");

    // 执行授权查询
    AlipayAuthConsultResponse alipayAuthConsultResponse;
    try {
        alipayAuthConsultResponse = CLIENT.execute(alipayAuthConsultRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // 处理错误情况
    }
}

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

copy
{
  "authRedirectUrl": "https://www.alipay.com",
  "authState": "STATE-2348729823419",
  "customerBelongsTo": "ANTOM_BIZ_ACCOUNT",
  "scopes": [
    "AGREEMENT_PAY"
  ],
  "terminalType": "WEB"
}

consult 接口的响应将返回包含买家需要跳转的授权链接(normalUrl)。涉及的参数如下

参数名称

是否必需

描述

normalUrl

将买家重定向到默认浏览器或嵌入式 WebView 中的 WAP 或网页的 URL。

result.resultCode

结果代码,指示调用接口的结果。

以下代码显示了响应报文的示例:

copy
{
    "normalUrl": "http://page.alipay.net/page/antom-web-checkout-v2/aba-page/pages/auto-debit/index.html?requestHost=http%3A%2F%2Fimgs-50.sggz00b.dev.alipay.net%2Fmgw.htm&sessionData=d%2BS5ZKdIDx%2BhKUl3jkhBVF9eQXMMS2tLO%2BhFc7BWVtOkjaQV982mcQW5GfOnCaGTlrpaD2EI5zsp2%2BUju6eEhg%3D%3D%26%26SG%26%26188&groupId=GROUP_20241001122147828",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

2. 跳转到授权链接 客户端

当您的服务器获取到 normalUrl 后,将该链接传给客户端。您的前端页面会执行将买家跳转到 normalUrl 指定的地址的操作。以下是客户端在 Web 终端加载 URL 的代码示例:

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

以下图片展示了支付方式签约页面的渲染效果:

image.png

步骤 2:获取授权结果

回跳商户页面

当买家在支付方式授权页面进行相关操作后,可能会发生授权成功或授权失败的情况。这两种情况下的后续跳转如下:

  • 授权成功:授权成功后,买家通常会跳转回商户页面,页面地址为 authRediectUrlauthCodeauthState 三个参数重构的 URL,但也有可能因为买家操作或者网络原因导致无法回跳。
  • 授权失败
    • 如果买家主动点击放弃授权等原因退出授权页面,部分支付方式支持买家回跳到商户页面,该商户页面地址为 authRediectUrl
    • 如果买家超时未授权或者授权失败则无法回跳到商户页面。

获取授权码

买家完成授权后,您可以通过以下方式之一获取授权码(authCode):

  • 从支付方式返回的重构链接中获取 authCode
  • 从 Antom 发送的异步授权通知中获取 authCode

从重构链接获取 authCode

在完成授权流程后,买家会跳转到支付方式返回的重构链接。此链接由以下三个部分组成:

  • 您在 consult 接口中指定的 authRedirectUrl 参数的值。
  • 该支付方式返回的 authCode 。
  • 您在 consult 接口中指定的 authState 参数的值。

以下是重构链接的示例:

copy
https://www.alipay.com/?authCode=d2f60253-ecdc-e9bc-27d1-566970191040&authState=663A8FA9-D836-48EE-8AA1-1FF682989DC7

您可以通过重构链接获取 authCode 值。但在使用 authCode 之前,需要检查重构 URL 中 authState 的值是否与 consult 接口传入的 authState 参数值一致,并进行以下处理:

  • 如果 authState 的值不一致,则该重构 URL 不可信,因为跳转过程中可能发生了被攻击等恶性事件,重构 URL 中的 authCode 不可用。
  • 如果 authState 的值一致,可以使用该 authCode 发起申请支付令牌请求。

常见问题

问:是否可以同时使用以上两种方式获取 authCode

答:是的,您可以同时通过重构 URL 和异步授权通知两种方式获取 authCode。如果您获得了多个 authCode,请使用最先收到的 authCode,在申请支付令牌时不要重复使用相同的 authCode 值。

问:如何判断授权失败?

答:如果等待超过 1 小时,您未能获取到 authCode,则可以判定本次授权失败。您可以重新引导买家进行授权。

步骤 3:申请支付令牌 服务端

在收到授权码authCode后 10 分钟内,调用 applyToken 接口来申请支付令牌(accessToken)。否则,authCode 将过期并失效。只有获得 accessToken,才能实现对买家账户的自动扣款。

调用 applyToken 接口时,请在请求中正确传入以下参数:

参数名称

是否必需?

描述

grantType

传入固定值为 AUTHORIZATION_CODE

customerBelongsTo

传入您请求授权的目标支付方式。

authCode

传入您收到的 authcode 值。

以下是调用 申请支付令牌 接口的代码示例:

copy
AlipayAuthApplyTokenRequest alipayPayRequest = new AlipayAuthApplyTokenRequest();
alipayPayRequest.setClientId(CLIENT_ID);
alipayPayRequest.setPath("/ams/sandbox/api/v1/authorizations/applyToken");

alipayPayRequest.setGrantType(GrantType.AUTHORIZATION_CODE);
alipayPayRequest.setCustomerBelongsTo(CustomerBelongsTo.Antom business account);
alipayPayRequest.setAuthCode("663A8FA9D83648EE8AA11FF68298XXXX");

//申请令牌
AlipayPayResponse alipayPayResponse = null;
try {
    alipayPayResponse = defaultAlipayClient.execute(alipayPayRequest);
} catch (AlipayApiException e) {
    String errorMsg = e.getMessage();
    // 处理错误情况
}

以下是请求报文示例

copy
{
  "authCode": "663A8FA9D83648EE8AA11FF68298XXXX",
  "customerBelongsTo": "ANTOM_BIZ_ACCOUNT",
  "grantType": "AUTHORIZATION_CODE"
}

在响应中,您将收到以下关键参数:

  • accessToken支付令牌。

以下是响应报文示例

copy
{
    "accessToken": "28208803011714191738919323000j9i8i8fU2f17100XXXX",
    "accessTokenExpiryTime": "2125-02-07T17:08:43+08:00",
    "extendInfo": "{\"userLoginId\":\"haiqing.XXX@antgroup.com\",\"userId\":\"218812021964XXXX\"}",
    "userLoginId": "haiqing.XXX@antgroup.com",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

以下为申请支付令牌的响应报文中 result.resultStatus 字段可能返回的值,您可以根据指引进行处理:

result.resultStatus

信息

后续操作

S

请求成功。

获取支付令牌成功,包含以下字段:

  • accessToken:表示由 Antom 生成的代扣令牌,用于后续支付。
  • accessTokenExpiryTime:令牌有效期,目前默认 100 年。
  • userLoginId: 表示买家在 Antom business account 中注册时使用的登录 ID。您可以存储该账号用于后续支付时展示。

U

未知问题。

建议使用原请求参数重新调用接口。如果问题未解决,请联系 Antom 技术支持。

F

请求失败。

建议根据 result.resultCode 的提示对相关问题进行处理。由于 authCode 只能使用一次,需要从步骤 1 重新开始,获取并跳转到授权链接。获取新的 authCode 后,再次调用 applyToken 接口

注意

  • 由于历史原因,以上关键参数适用于新商户,令牌有效期限为 100 年。若之前已经使用了accessTokenExpiryTimerefreshToken 和 refreshTokenExpiryTime 字段,您可以继续维持原有逻辑。
  • 如果您未收到响应报文,可能是网络超时所致,建议使用原请求参数重新调用接口。如果问题未解决,请联系 Antom 技术支持。
  • 如果调用接口后收到了响应,但响应中未返回支付令牌(accessToken),请采取以下措施:
    • 如果 result.resultStatus 的值为 U,请使用相同的参数和值再次发起请求。
    • 如果 result.resultStatus 的值为 F,根据 result.resultCode 提供的提示解决相关问题。如果需要再次调用 applyToken 接口获取支付令牌,由于 authCode 只能使用一次,需要从步骤 1 重新开始,获取授权链接,并跳转到授权链接。获取新的 authCode 后,再次调用 applyToken 接口。
  • 如果需要向买家显示授权账户,使用在 applyToken 接口中返回的 userLoginId 字段的值。该字段返回的值已经经过隐私保护处理,可以直接显示。

常见问题

问:是否支持异步通知获取支付令牌?

答:不支持。目前仅支持同步返回获取支付令牌。

问:令牌的有效期是多久?

答:100 年。

步骤 4:发起代扣 服务端

在获得买家授权后,您可以直接为买家提供代扣服务,无需他们在每次支付时都进行授权流程。

发起代扣时,请指定以下参数:

字段类型

字段名

是否必需?

描述

基本字段

paymentRequestId

由商户生成的专属 ID,每次发起支付时都会创建新 ID。
paymentAmount

您请求以订单币种接收的付款金额。

paymentRedirectUrl

支付完成后买方被重定向到的商家页面URL。
paymentNotifyUrl

用于接收支付结果通知的链接。 支付结果通知地址可以通过接口传输,或者在门户中设置固定值。

paymentMethod.paymentMethodType

付款方法选项中包含的付款方法类型。 

paymentMethod.paymentMethodId属于买方的付款方式的唯一 ID。 applyToken 接口获取的支付令牌(accessToken)的值。

订单字段

order.orderAmount

商户直接向客户提供的商品或服务金额。

order.referenceOrderId

商户系统中用于识别订单的唯一编号。

order.goods

货物信息,包括订单中货物的 ID、名称、价格和数量。需注意:

  • 在机票场景下,goodsName 的值必须格式化为 “机票-日期-航班号-出发-到达”。订单含多件商品时,请拆分成多个 goods 传入,每个 goodsName 传一个商品。
  • 在酒店场景下,goodsName 的值必须格式化为 “住宿时间(20250101-20250102),酒店名称”。订单含多件商品时,请拆分成多个 goods 传入,每个 goodsName 传一个商品。
  • orderAmount paymentAmount 的值等于所有 goods 的金额之和。 当一个订单中涉及多个产品时,请参考以下公式: goodsUnitAmount No.1 * goodsQuantity No.1 +goodsUnitAmount No.2 * goodsQuantity No.2 =orderAmount /paymentAmount。
  • referenceGoodsIdgoodsNamegoodsUnitAmount goodsQuantity 四个字段为必填项。
order.buyer

包括买家的 ID、姓名、电话号码和电子邮件的买家信息。传入该参数时,必须同时传入 referenceBuyerId buyerName.fullName

order.transit

行程信息,包括出行方式、行程段和乘客信息。在机票场景下,此字段必填。当 transit 信息为必填项,则 transitType legs.departureTimelegs.departureAddress.citylegs.arrivalAddress.city legs.carrierNo 字段为必传。

上述参数并不全面,请参阅 pay(令牌支付)接口以获取完整参数列表以及特定支付方式的额外要求。

以下是不同场景下请求报文的代码示例:

机票场景

copy
{
    "order": {
        "transit": {
            "transitType": "FLIGHT",
            "legs": [
                {
                    "departureTime": "2024-11-27T12:01:01+08:00",
                    "departureAddress": {
                        "city": "SZX" //需要符合IATA三位码
                    },
                    "arrivalAddress": {
                        "city": "HKG" //需要符合IATA三位码
                    },
                    "carrierNo": "111"
                }
            ]
        },
        "orderAmount": {
            "currency": "CNY",
            "value": "1000"
        },
        "orderDescription": "Cappuccino #grande (Mika's coffee shop)",
        "referenceOrderId": "ORDER_2022111414171****",
        "buyer": {
            "referenceBuyerId": "test1234****",
            "buyerName": {
                "fullName": "Dehua Skr Liu"
            }
        },
        "goods": [
            {
                "referenceGoodsId": "GoodsId-32078",
                "goodsUnitAmount": {
                    "currency": "CNY",
                    "value": "1000"
                },
                "goodsQuantity": "1",
                "goodsName": "机票-日期-航班号-出发-到达"
            }
        ]
    },
    "env": {
        "terminalType": "WEB"
    },
    "paymentAmount": {
        "currency": "CNY",
        "value": "1000"
    },
    "paymentMethod": {
        "paymentMethodType": "ANTOM_BIZ_ACCOUNT",
        "paymentMethodId": "28208803000517871730802054000MyNIvBt2HK17100****"
    },
    "settlementStrategy": {
        "settlementCurrency": "CNY"
    },
    "paymentNotifyUrl": "https://www.alipay.com/notify",
    "paymentRequestId": "PAY_2022111414171****",
    "productCode": "AGREEMENT_PAYMENT"
}

以下为响应示例代码:

copy
{
  "actualPaymentAmount": {
    "currency": "HKD",
    "value": "1000"
  },
  "paymentAmount": {
    "currency": "HKD",
    "value": "1000"
  },
  "paymentCreateTime": "2024-10-21T21:00:53-07:00",
  "paymentId": "2024102219401080010018874020964****",
  "paymentRequestId": "paymentRequestId_172956965****",
  "paymentTime": "2024-10-21T21:01:08-07:00",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

步骤 5:获取支付结果 服务端

买家完成支付或支付超时后,可以通过以下方式之一获取相应的支付结果:

  • 接收来自 Antom 的异步通知
  • 主动查询支付结果

查询支付结果

发起 inquiryPayment 请求,并传入以下参数:

参数名称

是否必需?

描述

paymentRequestId

由商户生成的支付请求 ID。

以下是示例代码:

copy
{
  "paymentRequestId": "10152024052741013451092402****"
}

以下为 inquiryPayment 响应的代码示例:

copy
{
  "actualPaymentAmount": {
    "currency": "HKD",
    "value": "1000"
  },
  "paymentAmount": {
    "currency": "HKD",
    "value": "1000"
  },
  "paymentId": "2024052719401080010018842022553****",
  "paymentMethodType": "ANTOM_BIZ_ACCOUNT",
  "paymentRedirectUrl": "https://xxx.com/bgt_launch_app_callback.html?browser_callback_new=1&chTransId=SO000124052709420662253642020002",
  "paymentRequestId": "10152024052741013451092402****",
  "paymentResultCode": "SUCCESS",
  "paymentResultMessage": "success",
  "paymentStatus": "SUCCESS",
  "paymentTime": "2024-05-27T02:27:27-07:00",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

通过 result 字段返回接口调用结果。订单状态需要根据 paymentStatus 来判断:

  • SUCCESS:表示支付成功。
  • FAIL:表示支付失败。
  • PROCESSING:表示支付处理中。
  • CANCELLED:表示支付已经被取消。

接收异步通知

完成支付或支付失败时,Antom 会通过 pay(令牌支付)接口中的 paymentNotifyUrl 参数指定的地址发送异步通知(notifyPayment),您也可以在 Antom Dashboard 中配置该地址。

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

copy
{
  "actualPaymentAmount": {
    "currency": "HKD",
    "value": "1000"
  },
  "notifyType": "PAYMENT_RESULT",
  "paymentAmount": {
    "currency": "HKD",
    "value": "1000"
  },
  "paymentCreateTime": "2024-05-27T02:27:13-07:00",
  "paymentId": "2024052719401080010018842022553****",
  "paymentRequestId": "10152024052741013451092402****",
  "paymentResultInfo": {},
  "paymentTime": "2024-05-27T02:27:27-07:00",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

当您收到 Antom 的异步通知,您需要按照返回收到确认信息的格式返回响应,但无需做加签处理。您需要对 Antom 发送的支付通知进行验签,关于如何验证通知的签名并做出响应,请参阅处理通知

无论订单是否支付成功,每个通知请求均需按以下固定格式响应。否则,Antom 会重新发送异步通知。

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

常见问题

问:什么时候会发送通知?

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

  • 如果支付成功完成,Antom 通常会在 3 到 5 秒内发送异步通知。
  • 如果支付未完成,Antom 需要先关闭订单,然后发送异步通知。代扣默认关单时间是 1 分钟。

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

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

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

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

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

答:如果您收到 Antom 的异步通知,您需要按照返回收到确认信息的格式返回响应,但无需做加签处理。

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

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

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

问:如果买家的 Antom bussiness account 里的币种和支付请求传入的币种不一致,交易是否会失败?

答:交易会失败。

支付后

完成支付后,您可对交易进行以下支付后的操作:

取消授权

授权完成后,您需为买家提供授权协议取消功能。欲了解更多详情,参见取消授权

取消交易

下单后您可以在一定时间窗口期内通过使用 cancel 接口主动关闭某笔交易,支付成功后可在 T+1日的 00:15(UTC+8:00)之前取消(T 为交易日)。具体操作参见取消交易

退款

在支付成功后,您可以通过调用 refund 接口对成功支付的交易发起退款。具体操作参见退款

对账

交易完成后,您可以使用 Antom 提供的财务报告进行对账。请参阅对账了解关于如何对账和 Antom 结算规则的更多信息。

最佳实践

Antom 为您提供支付结果展示、支付失败重试、接口超时时间设置等最佳实践方案,请参阅最佳实践了解详情。