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

代扣(Web)

本文介绍了支持桌面浏览器自动扣款的集成方案。集成后,您可以访问各种支付方式,如电子钱包、银行卡和银行转账。

用户体验

二维码扫描的用户体验

在扫码授权模式下,买家跳转到支付方式 Web 页面,通过扫描页面二维码进行授权。如下图所示:

扫码授权.png

支付流程

在获得买家授权后,您可以直接发起代扣服务,无需为每次支付再次进行授权流程。

在进行自动扣款之前,您需要首先获得买家的授权。一旦买家同意授权,使用授权码获取支付令牌,用于后续的代扣操作。

1.png

集成步骤

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

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

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

您需要先获取授权链接,然后将买家跳转到授权链接以完成授权。对于 Web 集成,授权链接可以从 授权咨询 接口响应中返回的 normalUrl 中获取。

获取授权链接

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

安装接口库

GitHub 上查找最新版本。

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

初始化请求实例

copy
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);

}

调用 咨询 接口

授权咨询 请求中传入以下参数:

字段名称

是否为必填字段?

描述

authRedirectUrl

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

authState

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

customerBelongsTo

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

scopes

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

terminalType

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

以下代码示例用于调用 授权咨询 接口以获取授权链接:

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

    // replace with your authState
    String authState = UUID.randomUUID().toString();
    alipayAuthConsultRequest.setAuthState(authState);

    // set the target payment method for which you are requesting authorization
    alipayAuthConsultRequest.setCustomerBelongsTo(CustomerBelongsTo.GCASH);

    // set the authorization scope
    alipayAuthConsultRequest.setScopes(new ScopeType[]{ScopeType.AGREEMENT_PAY});

    // set terminalType
    alipayAuthConsultRequest.setTerminalType(TerminalType.WEB);

    // replace with your authRedirectUrl
    alipayAuthConsultRequest.setAuthRedirectUrl("http://www.yourRedirectUrl.com");

    // do authorization consult
    AlipayAuthConsultResponse alipayAuthConsultResponse;
    try {
        alipayAuthConsultResponse = CLIENT.execute(alipayAuthConsultRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // handle error condition
    }
}

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

copy
{
    "authRedirectUrl": "http://www.yourRedirectUrl.com",
    "authState": "556c1e32-3723-4b02-88ed-8c46087540ca",
    "customerBelongsTo": "GCASH",
    "scopes": [
        "AGREEMENT_PAY"
    ],
    "terminalType": "WEB"
}

接收咨询响应

授权咨询 接口的响应包含买家需要跳转的授权链接(normalUrl)

copy
{
    "authCodeForm": {
        "codeDetails": [
            {
                "codeValue": "https://iexpfront-sea.alipay.com/showQrImage.htm?logoImg=a***",
                "displayType": "IMAGE"
            },
            {
                "codeValue": "https://open-sea-global.alipayplus.com/aps/long/vbxsTndnmS",
                "displayType": "TEXT"
            }
        ]
    },
    "normalUrl": "https://g.alipayplus.com/page/aplus-linker/acwallet/authorization.html?acqSiteId=1***",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

常见问题

问:如何设置 terminalType

答:terminalType 的有效值如下:

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

问:请求字段值可以使用中文字符吗?

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

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

答:Antom 通过 支付通知 接口发送支付结果,您可以 Antom Dashboard 中提供接收通知的地址。

问:收到返回的 normalUrl 后应该怎么做?

答:对于在桌面浏览器中进行的交易, Antom 授权咨询 响应中返回 normalUrl 参数。 您的服务器需要传入此链接给客户端进行跳转。建议不要缓存返回的 normalUrl,当重新发起授权时,需要获取新的 normalUrl 用于跳转

跳转到授权链接

当您的服务器获取到 normalUrl 后,将该链接传给客户端。您的前端页面会执行将买家跳转到 normalUrl 指定的地址的操作。授权过程可能导致授权成功或失败,每种情况下的跳转地址都不同:

  • 授权成功:授权成功后,买家跳转到由 authRedirectUrlauthCode authState 的值重构的链接指向的一个页面。
  • 授权失败:
    • 如果买家在未完成授权流程的情况下离开授权页面,某些支付方式允许跳转到 authRedirectUrl 指向的页面。
    • 如果授权超时或失败,且买家无法跳转到 authRedirectUrl 页面,建议引导买家重新开始授权流程。如果买家在支付方式应用内未能在 15 分钟内跳转到 authRedirectUrl 页面,授权将失败。

注意授权链接只能使用一次,如果买家授权失败,您需要重新调用 授权咨询 接口并提供一个新的 authState 值。

步骤 2:获取授权结果

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

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

从重构链接获取 authCode

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

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

以下是重构链接的示例:

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

您可以通过重构链接获取 authCode 值。但在使用 authCode 之前,需要检查重构链接中 authState 的值是否与 授权咨询 接口中指定的 authState 参数值一致。如果不一致,重构链接中的 authCode 不可用,因为可能存在攻击等恶意事件导致跳转过程中的链接不安全。

从授权通知获取 authCode

由于网络问题或其他原因,您可能无法获取重构链接。此时,您可以按照以下步骤从Antom 发送的授权通知中获取 authCode:

  1. 配置接收异步授权通知的地址,请参阅通知了解更多详情
  2. 当买家同意授权后,您将收到 Antom 发送的授权成功通知
  3. 收到异步通知后,按照要求所示的格式向 Antom 发送响应。否则,Antom 会重新发送异步通知。

注意:您可以通过重构链接或授权通知获取 authCode。如果您收到了多个 authCodes,请使用最先收到的 authCode在申请支付令牌时不要重复使用相同的 authCode 值。

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

copy
{
    "authorizationNotifyType": "AUTHCODE_CREATED",
    "authState": "556c1e32-3723-4b02-88ed-8c46087540ca",
    "authCode": "28100113_1631148338197000019ba74",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success",
        "resultStatus": "S"
    }
}

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

常见问题

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

答:买家授权完成后,Antom 会向您发送异步通知。

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

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

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

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

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

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

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

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

  • authorizationNotifyType通知类型。 在此情况下值为 AUTHCODE_CREATED
  • authState调用 授权咨询 接口时提供的请求 ID。
  • authCode用户完成授权后生成的授权码,使用此授权码来请求支付令牌。

步骤 3:申请支付令牌

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

调用 申请支付令牌 接口时,请在请求中正确传入以下参数:

字段名称

是否为必填字段?

描述

grantType

传入固定值为 AUTHORIZATION_CODE

customerBelongsTo

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

authCode

传入您收到的 authcode 值。

以下是申请支付令牌的代码示例:

copy
public static void applyToken() {
    String authCode = "yourAuthCode";
    AlipayAuthApplyTokenRequest alipayPayRequest = new AlipayAuthApplyTokenRequest();

    // set grant type
    alipayPayRequest.setGrantType(GrantType.AUTHORIZATION_CODE);
    // set the target payment method for which you are requesting authorization
    alipayPayRequest.setCustomerBelongsTo(CustomerBelongsTo.GCASH);
    // set auth code
    alipayPayRequest.setAuthCode(authCode);

    // do apply token
    AlipayAuthApplyTokenResponse authApplyTokenResponse;
    try {
        authApplyTokenResponse = CLIENT.execute(alipayPayRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // handle error condition
    }
}

以下是申请支付令牌的请求报文示例

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

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

  • accessToken支付令牌。

注意

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

关于代扣支持的支付方式,支付令牌有效期如下表所示:

支付方式

支付令牌有效期

Alipay

92年

Kakao Pay

100

AlipayHK

100

GCash

100

DANA

100

Touch'n Go eWallet

100

TrueMoney

100

PayPay

100

NAVER Pay

2

Boost

100

KrungThai Bank

76

Siam Commercial Bank

76

Grabpay SG

10

Grabpay MY/PH

10

K PLUS

100

Maya

100

LINE Pay

100

Toss Pay

14

ZaloPay

10

步骤 4:发起代扣

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

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

字段名称

是否为必填字段?

描述

productCode

在此场景中,此字段设置为 CASHIER_PAYMENT

paymentRequestId

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

paymentAmount

支付金额需设置为单一币种的最小单位,如 CNY 为分,KRW 为韩元。

paymentMethod

支付方式枚举值。

paymentMethod.paymentMethodId

申请支付令牌 接口获取的支付令牌(accessToken)的值

paymentNotifyUrl

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

settlementStrategy

此支付订单的结算币种。如果业务已签署多个结算币种,需要在接口中指定。

order

包括商户订单金额、订单号、订单描述等信息的订单详情。

env

买家发起交易的终端类型。例如,交易从商户 PC 网站发起,值为 env.terminalType = WEB

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

以下为发起代扣的示例代码

copy
public static void pay() {
    AlipayPayRequest alipayPayRequest = new AlipayPayRequest();
    alipayPayRequest.setProductCode(ProductCodeType.AGREEMENT_PAYMENT);

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

    // set amount
    // you should convert amount unit(in practice, amount should be calculated on your serverside)
    Amount amount = Amount.builder().currency("SGD").value("550000").build();
    alipayPayRequest.setPaymentAmount(amount);

    // set paymentMethod
    PaymentMethod paymentMethod = PaymentMethod.builder().paymentMethodType("GCASH").
            paymentMethodId("2828XXX77801726307481000Iba1Pm20IU171000179").build();
    alipayPayRequest.setPaymentMethod(paymentMethod);

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

    // replace with your orderId
    String orderId = UUID.randomUUID().toString();
    // set order Info
    Order order = Order.builder().referenceOrderId(orderId).
            orderDescription("antom api testing order").orderAmount(amount).buyer(buyer).build();
    alipayPayRequest.setOrder(order);

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

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

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

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

copy
{
    "env": {
        "clientIp": "114.121.121.01",
        "terminalType": "WEB"
    },
    "order": {
        "buyer": {
            "referenceBuyerId": "yourBuyerId"
        },
        "orderAmount": {
            "currency": "SGD",
            "value": "550000"
        },
        "orderDescription": "antom api testing order",
        "referenceOrderId": "f69cb774-8d47-4da9-bf91-08c656581cdf"
    },
    "paymentAmount": {
        "currency": "SGD",
        "value": "550000"
    },
    "paymentMethod": {
        "paymentMethodId": "2828XXX77801726307481000Iba1Pm20IU171000179",
        "paymentMethodType": "GCASH"
    },
    "paymentNotifyUrl": "http://www.yourNotifyUrl.com",
    "paymentRequestId": "214c60e7-672d-4ad8-9163-905ad4abe71f",
    "productCode": "AGREEMENT_PAYMENT"
}

常见问题

问:如何设置 terminalType

答:terminalType 的有效值为:

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

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

答:为了避免特定支付方式的不兼容情况,请勿在请求字段中使用中文字符。

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

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

步骤 5:获取支付结果

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

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

接收异步通知

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

以下是通知的代码示例

copy
{
    "actualPaymentAmount": {
        "currency": "SGD",
        "value": "550000"
    },
    "notifyType": "PAYMENT_RESULT",
    "paymentAmount": {
        "currency": "SGD",
        "value": "550000"
    },
    "paymentCreateTime": "2024-09-14T02:59:28-07:00",
    "paymentId": "202409141940108001001888H0209958443",
    "paymentRequestId": "214c60e7-672d-4ad8-9163-905ad4abe71f",
    "paymentResultInfo": {},
    "paymentTime": "2024-09-14T02:59:30-07:00",
    "pspCustomerInfo": {
        "pspCustomerId": "use***@alipay.com",
        "pspName": "GCASH"
    },
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

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

以下是通知响应的代码示例

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

常见问题

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

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

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

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

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

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

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

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

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

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

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

  • result:表示订单的支付结果。
  • paymentRequestId:用于咨询、取消和对账的支付请求 ID。
  • paymentIdAntom 生成的支付订单 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
{
    "actualPaymentAmount": {
        "currency": "SGD",
        "value": "550000"
    },
    "paymentAmount": {
        "currency": "SGD",
        "value": "550000"
    },
    "paymentId": "202409141940108001001888H0209958443",
    "paymentRequestId": "214c60e7-672d-4ad8-9163-905ad4abe71f",
    "paymentResultCode": "SUCCESS",
    "paymentResultMessage": "success",
    "paymentStatus": "SUCCESS",
    "paymentTime": "2024-09-14T02:59:30-07:00",
    "pspCustomerInfo": {
        "pspName": "GCASH"
    },
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

常见问题

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

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

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

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

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

最佳实践

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

配置授权链接

在调用 授权咨询 接口后,根据授权过程使用的支付方式,接口响应中可能会返回不同类型的授权链接。关于每种支付方式返回的授权链接的详细信息,请参阅 部分支付方式返回的链接

对于终端类型为 WEB 的情况,normalUrl 将在 授权咨询 接口的响应中返回。

打开 normalUrl

获取到 normalUrl 值后,您需要使用该 normalUrl 指定的授权链接将买家跳转到授权页面。建议在新标签页中打开链接,引导买家完成授权。打开新页面的代码如下:

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

使用 normalUrl 渲染的授权页面可能是二维码扫描页或登录页。以下图表展示了这两种情况下的授权流程:

image.png