代扣(WAP)
本文介绍了支持移动浏览器自动扣款的集成方案。集成后,您可以访问各种支付方式,如电子钱包、银行卡和银行转账。
用户体验
买家从移动浏览器中的商户页面跳转到同一浏览器中的支付方式页面。
支付流程
在获得买家授权后,您可以直接发起代扣服务,无需为每次支付再次进行授权流程。
在进行自动扣款之前,您需要首先获得买家的授权。一旦买家同意授权,使用授权码获取支付令牌,用于后续的代扣操作。
集成步骤
在进行代扣之前,需要获得买家的授权。授权成功后,获取授权码为后续代扣请求支付令牌。通过以下集成步骤,可以获取买家授权并进行代扣操作:
- 获取并跳转到授权链接
- 获取授权结果
- 申请支付令牌
- 发起代扣
- 获取支付结果
步骤 1:获取并跳转到授权链接
首先您需要获取授权链接,然后将买家跳转到授权链接以完成授权。
获取授权链接
Antom 提供了多种语言的服务器端接口库。以下代码以 Java 为例,您需要安装 Java 6 及更高版本。
安装接口库
在 GitHub 上查找最新版本。
<dependency>
<groupId>com.alipay.global.sdk</groupId>
<artifactId>global-open-sdk-java</artifactId>
<version>2.0.44</version>
</dependency>
初始化请求实例
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 | 是 | 在此场景中,该字段设置为 |
terminalType | 是 | 表示商户端所在的终端类型。 |
以下为调用 授权咨询 接口以获取授权链接的代码示例:
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.WAP);
alipayAuthConsultRequest.setOsType(OsType.ANDROID);
// 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
}
}
以下为请求报文的代码示例:
{
"authRedirectUrl": "http://www.yourRedirectUrl.com",
"authState": "b2983991-9ab6-4bad-8cc8-092581c39950",
"customerBelongsTo": "GCASH",
"osType": "ANDROID",
"scopes": [
"AGREEMENT_PAY"
],
"terminalType": "WAP"
}
接收咨询响应
返回参数 normalUrl ,您需要将买家跳转到 normalUrl 参数指定的地址。
{
"appIdentifier": "com.iap.linker_portal",
"normalUrl": "https://ac.alipay.com/page/sandbox/common-auth-pMiddle.html?acqSiteId=102XXX",
"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 指定的地址的操作。授权过程可能导致授权成功或失败,每种情况下的跳转地址都不同:
- 授权成功:授权成功后,买家跳转到由 authRedirectUrl、authCode 和 authState 的值重构链接指向的页面。
- 授权失败:
- 如果买家离开授权页面,某些支付方式允许买家跳转到商户页面,商户页面的地址由 authRedirectUrl 的值指定。
- 如果买家因授权超时或失败,未能从支付方式应用跳转到商户页面,建议引导买家重新启动授权流程。通常,如果买家在 15 分钟后仍未能从支付方式应用跳转到商户页面,授权将失败。
注意:授权链接只能使用一次,如果买家授权失败,您需要重新调用 授权咨询 接口并提供一个新的 authState 值。
步骤 2:获取授权结果
买家完成授权后,您可以通过以下方式之一获取授权码(authCode):
- 从支付方式返回的重构链接中获取授权码。
- 从 Antom 发送的异步授权通知中获取授权码。
从重构链接获取 authCode
在完成授权流程后,买家会跳转到支付方式返回的重构链接。此链接由以下三个部分组成:
以下是重构链接的示例:
https://www.alipay.com/?authCode=d2f60253-ecdc-e9bc-27d1-566970191040&authState=663A8FA9-D836-48EE-8AA1-1FF682989DC7
您可以通过重构链接获取 authCode 值。但在使用 authCode 之前,需要检查重构链接中 authState 的值是否与 授权咨询 接口中指定的 authState 参数值一致。如果不一致,重构链接中的 authCode 不可用,因为可能存在攻击等恶意事件导致跳转过程中的链接不安全。
从授权通知中获取 authCode
由于网络问题或其他原因,您可能无法获取重构链接。此时,您可以按照以下步骤从Antom 发送的授权通知中获取 authCode:
- 配置接收异步授权通知的地址,请参阅通知了解更多详情 。
注意:您可以通过重构链接或授权通知获取 authCode。如果您收到了多个 authCodes,请使用最先收到的 authCode,在申请支付令牌时不要重复使用相同的 authCode 值。
以下是通知请求的代码示例:
{
"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 | 是 | 传入固定值 |
customerBelongsTo | 是 | 指定您请求授权的目标支付方式。 |
authCode | 是 | 传入您获取到的 authcode 值。 |
以下是申请支付令牌的代码示例:
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
}
}
以下是申请支付令牌的请求报文示例:
{
"authCode": "663A8FA9D83648EE8AA11FF68298XXXX",
"customerBelongsTo": "GCASH",
"grantType": "AUTHORIZATION_CODE"
}
在响应中,您将收到以下关键参数:
- accessToken:支付令牌。
注意:
- 由于历史原因,以上关键参数适用于新商户,同时请参阅下表了解更多令牌有效期信息。若之前已经使用了accessTokenExpiryTime、refreshToken 和 refreshTokenExpiryTime 字段,您可以继续维持原有逻辑。
- 如果调用接口后未收到响应,建议使用相同的参数和值再次发起请求。
- 如果调用接口后收到了响应,但响应中未返回支付令牌(accessToken),请采取以下措施:
- 如果需要向买家显示授权账户,使用在 申请支付令牌 接口中返回的 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 | 是 | 在此场景中,此字段设置为 |
paymentRequestId | 是 | 由商户生成的专属 ID,每次发起支付时都会创建新 ID。 |
paymentAmount | 是 | 支付金额需设置为单一币种的最小单位,如 |
paymentMethod | 是 | 支付方式枚举值。 |
paymentMethod.paymentMethodId | 是 | 调用 申请支付令牌 接口获取的支付令牌(accessToken)的值 |
paymentRedirectUrl | 是 | 商户端的支付结果页面需要根据服务器端的结果来显示,不一定是成功页面。 |
paymentNotifyUrl | 否 | 支付结果通知地址可以通过接口传输,或者在门户中设置固定值。 |
settlementStrategy | 否 | 此支付订单的结算币种。如果业务已签署多个结算币种,需要在接口中指定。 |
order | 是 | 包括商户订单金额、订单号、订单描述等信息的订单详情。 |
env | 是 | 买家发起交易的终端类型。例如,交易从商户 PC 网站发起,值为 env.terminalType = |
上述参数并不全面,请参阅 支付(代扣) 接口以获取完整参数列表以及特定支付方式的额外要求。
以下为发起代扣的代码示例:
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.WAP).osType(OsType.ANDROID).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
}
}
以下是请求报文的代码示例:
{
"env": {
"clientIp": "114.121.121.01",
"osType": "ANDROID",
"terminalType": "WAP"
},
"order": {
"buyer": {
"referenceBuyerId": "yourBuyerId"
},
"orderAmount": {
"currency": "SGD",
"value": "550000"
},
"orderDescription": "antom api testing order",
"referenceOrderId": "63fbe4c6-2253-461b-a561-87946d270169"
},
"paymentAmount": {
"currency": "SGD",
"value": "550000"
},
"paymentMethod": {
"paymentMethodId": "2828XXX77801726307481000Iba1Pm20IU171000179",
"paymentMethodType": "GCASH"
},
"paymentNotifyUrl": "http://www.yourNotifyUrl.com",
"paymentRequestId": "0fdf99eb-4975-455a-92ec-e519d9c7bd53",
"productCode": "AGREEMENT_PAYMENT"
}
常见问题
问:如何设置 terminalType?
答:terminalType 的有效值为:
- 如果买家在 PC 端发起交易,需要将 terminalType 参数设置为
WEB
。- 如果买家在移动浏览器上发起交易,需要将 terminalType 参数设置为
WAP
。同时添加 osType 参数,并根据买家的手机系统填写相应的参数值ANDROID
或IOS
。- 如果买家在应用内发起交易,需要将 terminalType 参数设置为
APP
。问:请求参数的值可以使用中文字符吗?
答:为了避免某些支付方式的不兼容情况,请求中的字段请勿使用中文字符。
问:如何设置接收支付通知的链接?
答:在 支付会话创建(收银台)接口中指定 paymentNotifyUrl 以接收支付结果(支付通知)的异步通知,或者在 Antom Dashboard 中配置接收链接。如果请求和 Antom Dashboard 都指定了链接,则请求中指定的值优先。
步骤 5:获取支付结果
买家完成支付或支付超时后,您可以通过以下方式之一获取相应的支付结果:
- 接收来自Antom 的异步通知
- 主动查询支付结果
接收异步通知
完成支付或支付失败时,Antom 会通过 支付(代扣)接口中的 paymentNotifyUrl 参数指定的地址发送异步通知(支付通知),您也可以在 Antom Dashboard 中配置该地址。
以下是通知的代码示例:
{
"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"
}
}
关于如何验证通知的签名并作出响应,请参阅签名与验签。
以下是通知响应的代码示例:
{
"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。
- paymentId:由 Antom 生成的支付订单 ID,用于退款和对账。
- paymentAmount:表示支付金额。
查询支付结果
发起 支付结果查询 请求,并传入以下参数:
字段名称 | 是否为必填字段? | 描述 |
paymentRequestId | 是 | 商户生成的支付请求 ID。 |
以下为调用接口的代码示例:
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
}
}
以下为接口响应的代码示例:
{
"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 来判断订单状态:
SUCCESS
和FAIL
表示最终结果。PROCESSING
表示处理中。
- paymentAmount:表示支付的金额。
最佳实践
遵循以下最佳实践以提高集成效率。
配置授权链接
在 WAP 场景中,授权咨询 接口的响应中不同的支付方式可能会返回以下三个链接中的一个或全部:
- normalUrl
- applinkUrl
- schemeUrl
选择链接
如果 授权咨询 接口响应只包含三个链接中的一个,直接将收到的链接用作跳转链接。如果收到多个链接,我们建议您根据以下说明选择链接:
- 对于 iOS 系统:优先使用 applinkUrl,然后是 schemeUrl,最后是 normalUrl。
- 对于 Android 系统:优先使用 schemeUrl,然后是 applinkUrl,最后是 normalUrl。
打开链接
使用 JavaScript 的跳转方法打开链接,代码如下所示:
window.location.href = URL;