Antom, leading provider of tailored payment solutions

集成快捷支付(SDK)

快捷支付(EasySafePay)是一款专注于小额高频支付场景的极简支付解决方案,通过创新的“首次支付即绑卡”和一键支付功能,大幅简化支付流程,买家首次交易即可完成钱包绑定或直接支付,后续支付无需重复验证即可极速完成。
该方案依托行业领先的智能风控系统、动态轮询技术和支付失败挽回策略,在确保交易安全的同时将支付成功率提升至行业顶尖水平,实现了买家支付体验、商户转化率和平台生态价值的三方共赢。
Web/WAP

用户体验

通过简单的浏览器集成,可快速接入电子钱包和网银转账支付功能。
各支付方式在浏览器端的用户体验存在差异,具体交互体验详见下表:
支付方式类别
支付方式
端类型
首次支付
后续支付
电子钱包
Web
在支付方式应用程序内完成支付。
在商户页面完成支付。
网银转账
Web
在支付方式页面完成支付。
在支付方式页面完成支付。
电子钱包
WAP
在商户页面完成支付。
在商户页面完成支付。
WAP
跳转至支付方式应用程序完成支付。
在商户页面完成支付。
网银转账
WAP
在商户页面完成支付。
在商户页面完成支付。
电子钱包
电子钱包
电子钱包支付场景下,买家确认支付后,系统将引导买家在商户页面或支付方式应用程序内完成交易。首次支付验证与后续支付简化流程如图所示:
首次支付
首次支付需完成安全验证,授权后即可享受免密支付。
后续支付
后续支付仅需提交订单即可完成免密支付。
网银转账
网银转账
网银转账支付场景下的体验流程如图所示,包含首次支付验证与后续支付简化流程。
首次支付
首次支付需完成安全验证,授权后即可享受免密支付。
后续支付
后续支付仅需提交订单即可完成免密支付。

支付流程

首次支付
首次支付流程示意图:
  1. 买家进入商户收银台页面
  2. 创建支付会话请求
    买家确认支付后,调用
    createPaymentSession(快捷支付) 接口获取支付会话。
  3. 调用客户端 SDK
    使用支付会话调用 SDK,SDK 将根据支付方式特性自动采集支付要素、渲染支付界面、处理页面跳转并引导买家完成支付。不同支付方式的交互差异详见用户体验。
  4. 获取授权结果
    当授权成功时,Antom 会通过 notifyAuthorization 接口向您发送异步通知。
  1. 获取支付结果
    可通过以下方式同步支付状态:
后续支付
后续支付流程示意图:
  1. 买家进入商户收银台页面
  2. 创建支付会话请求
    买家确认支付后,调用
    createPaymentSession(快捷支付) 接口获取支付会话。
  3. 调用客户端 SDK
    使用支付会话调用 SDK,SDK 将根据支付方式特性自动采集支付要素、渲染支付界面、处理页面跳转并引导买家完成支付。不同支付方式的交互差异详见用户体验。
  4. 获取支付结果
    可通过以下方式同步支付状态:

集成准备

在您开始集成前,请阅读集成指南及接口概述文档,了解服务端接口的集成步骤及调用接口的注意事项,并确保已完成以下预配置:
  • 已获得 client ID。
  • 已完成密钥配置。
  • 已完成异步通知接收地址的配置。
  • 集成 Antom 服务端 SDK 资源包,并完成接口库安装及请求示例初始化。具体操作请参阅服务端 SDK。
  • 如需使用沙箱环境与生产环境进行联调测试,请提前两个工作日联系 Antom 技术支持申请配置。沙箱环境和生产环境需单独配置。
参阅 Web/WAP 端集成 SDK 资源包文档来集成客户端 SDK 资源包。

集成步骤

请按以下流程开始您的集成:
  1. (可选)预加载
  2. 创建支付会话
  3. 调用 SDK
  4. 获取授权和支付结果

(可选)步骤 1:预加载 SDK
客户端

在加载收银台列表页面时,执行预加载可显著提升收银台页面的渲染性能且无负面性能影响。建议在买家选择支付方式时触发预加载。
请参考以下代码实现预加载:
AMSEasyPay.preload();

步骤 2:创建支付会话
服务端

当买家选择由 Antom 提供的支付方式进行支付时,您需要收集支付请求 ID、订单金额、支付方式、订单描述、支付重定向页面链接和支付结果通知链接等必要信息。
调用 createPaymentSession(快捷支付) 接口发起支付会话创建,并使用响应中返回的 paymentSessionData 初始化 Antom SDK。
首次支付和后续支付调用 createPaymentSession(快捷支付) 接口参数说明:
注意:Express Bank Transfer 的首次支付与后续支付使用相同的传参格式,买家只需在支付页面输入手机号即可完成支付。
类型
字段
是否必传
(首次支付)
是否必传
(后续支付)
描述


基础字段
productCode
是
是
EasySafePay 场景下值为:AGREEMENT_PAYMENT。
productScene
是
是
EasySafePay 场景下值为:EASY_PAY。
paymentRequestId
是
是
商户为识别支付请求而分配的专属 ID。
paymentMethod.paymentMethodType
是
是
授权签约的支付方式。
paymentMethod.paymentMethodId
否
是
传入首次支付后在 notifyAuthorization 接口中获得的accessToken 的值。
settlementStrategy
否
否
该笔支付的结算策略。若业务签约了多个结算币种,需在接口中指定。
paymentAmount.value
是
是
支付金额,需按下单币种的最小单位设置,请参考常见币种的最小单位。
paymentAmount.currency
是
是
符合 ISO 4217 标准的三位币种代码。
paymentSessionExpiryTime
否
否
支付会话过期的特定日期和时间。默认过期时间为会话创建后 1 小时。最大不能超过1小时。例如,如果会话在 2023-7-27T12:00:01+08:30 创建,那么默认会话过期时间为 2023-7-27T13:00:01+08:30。
paymentRedirectUrl
是
是
商户端支付结果页,需根据服务端结果展示,非固定为成功页。
paymentNotifyUrl
否
否
异步通知地址,必须为HTTPS地址。如果已通过门户配置,则该字段可不传。
授权字段
agreementInfo.authState
是
否
用于发起授权而分配的专属 ID。该参数仅在首次支付时传递,后续支付无需传递此参数。

订单字段



order.orderAmount
是
是
订单金额。
order.referenceOrderId
是
是
商户端订单号。
order.orderDescription
是
是
商户端订单描述。
order.buyer.referenceBuyerId
否
否
传入买家信息以供风险决策。传入 referenceBuyerId、buyerPhoneNo、buyerEmail 其中一个即可。

order.buyer
    • buyerPhoneNo
    • buyerEmail
否
否
买家支付账户。提供支付账户相关信息可有效提升支付成功率并降低买家流失风险。具体格式要求请见下表。
各支付方式买家支付账户传参格式如下:
支付方式
传参格式
建议优先传入手机号。手机号格式如下:
  • 手机号码:
    • 中国香港:852-12****78,注意手机号总长度是 8 位(不包含前缀 852-)。
    • 中国大陆:86-137****5678,注意手机号总长度是 11 位(不包含前缀 86-)。
  • 邮箱:xxx@xxx.com。
62-8569****7288,手机号 8 开头且手机号长度不超过 12 位(不包括前缀 62-)。
TrueMoney
买家需要在支付方式侧认证,不需要传入。
首次支付时传入买家支付账号可自动回填买家账号至支付页面,避免手动输入操作。以下是传入和未传入的体验对比图:
传入买家支付账号
无需输入登录账号。
未传入买家支付账号
需手动输入登录账号。
以下是首次支付和后续支付两种不同场景集成代码示例:
首次支付
@PostMapping("/payment/createSession")
public ResponseEntity<ApiResponse> createPaymentSession(@RequestBody PaymentVO payment) {
    AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
    alipayPaymentSessionRequest.setProductCode(ProductCodeType.AGREEMENT_PAYMENT);
    alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.EASY_PAY);

    // 替换为您的 paymentRequestId
    String paymentRequestId = UUID.randomUUID().toString();
    alipayPaymentSessionRequest.setPaymentRequestId(paymentRequestId);

    // 转换金额单位(实际使用中,金额应在服务端计算)
    // 详情请参考:<a href="https://docs.antom.com/ac/ref/cc">金额对象使用规则</a>
    long amountMinorLong = Money.of(CurrencyUnit.of(payment.currency), new BigDecimal(payment.amountValue)).getAmountMinorLong();

    // 设置金额
    Amount amount = Amount.builder().currency(payment.currency).value(String.valueOf(amountMinorLong)).build();
    alipayPaymentSessionRequest.setPaymentAmount(amount);

    // 设置结算策略
    // 替换为您现有的结算货币
    SettlementStrategy settlementStrategy = SettlementStrategy.builder().settlementCurrency("USD").build();
    alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

    User loginUser = users.get(payment.getUserId());

    // 设置买家信息
    Buyer buyer = Buyer.builder().referenceBuyerId("yourBuyerId").build();

    // 设置 paymentMethod
    PaymentMethod paymentMethod = PaymentMethod.builder().paymentMethodType(payment.paymentMethodType).build();
    if (loginUser.getPaymentMethodTypeAccessToken().containsKey(payment.getPaymentMethodType())) {
        // 买家已授权
        String accessToken = loginUser.getPaymentMethodTypeAccessToken().get(payment.getPaymentMethodType());
        paymentMethod.setPaymentMethodId(accessToken);
    } else {
        // 设置 agreementInfo
        // 替换为您的 authState
        String authState = UUID.randomUUID().toString();
        AgreementInfo agreementInfo = AgreementInfo.builder().authState(authState).build();
        alipayPaymentSessionRequest.setAgreementInfo(agreementInfo);

        // 保存与 authState 对应的 paymentMethodType
        authStatePayment.put(authState, payment);

        // 买家在支付方式客户端注册时使用的登录 ID,登录 ID 可以是买家的电子邮箱地址或手机号码
        // 指定此参数可免去买家手动输入登录 ID
        if(StringUtil.isNotBlank(loginUser.getPhoneNumber()){
            buyer.setBuyerPhoneNo(loginUser.getPhoneNumber());
        }
        if(StringUtil.isNotBlank(loginUser.getEmail() && "ALIPAY_HK".equals(payment.getPaymentMethodType())){
            buyer.setBuyerPhoneNo(loginUser.getPhoneNumber());
        }
    }
    alipayPaymentSessionRequest.setPaymentMethod(paymentMethod);



    // 替换为您的 orderId
    String orderId = UUID.randomUUID().toString();
    // 设置订单信息
    Order order = Order.builder().referenceOrderId(orderId)
    .orderDescription("antom sdk testing order").orderAmount(amount).buyer(buyer).build();
    alipayPaymentSessionRequest.setOrder(order);

    // 替换为您的通知 URL
    // 或在此配置您的通知 URL:<a href="https://dashboard.antom.com/global-payments/developers/iNotify">通知URL</a>
    alipayPaymentSessionRequest.setPaymentNotifyUrl("http://www.yourNotifyUrl.com/payment/receivePaymentNotify");

    // 替换为您的重定向 URL
    alipayPaymentSessionRequest.setPaymentRedirectUrl(
            "http://localhost:8080/index.html?paymentRequestId=" + paymentRequestId);

    AlipayPaymentSessionResponse alipayPaymentSessionResponse;
    try {
        long startTime = System.currentTimeMillis();
        System.out.println("payment request: " + JSON.toJSONString(alipayPaymentSessionRequest));
        alipayPaymentSessionResponse = CLIENT.execute(alipayPaymentSessionRequest);
        System.out.println("payment response: " + JSON.toJSONString(alipayPaymentSessionResponse));
        System.out.println("payment request cost time: " + (System.currentTimeMillis() - startTime) + "ms");
    } catch (AlipayApiException e) {
        return ResponseEntity.ok().body(new ApiResponse(paymentRequestId, payment.getUserId(), e));
    }
    return ResponseEntity.ok().body(new ApiResponse(paymentRequestId, payment.getUserId(), alipayPaymentSessionResponse));
}
请求报文示例:
{
  "agreementInfo": {
    "authState": "authState001"
  },
  "order": {
    "buyer": {
      "referenceBuyerId": "referenceBuyerId001",
      "buyerPhoneNo": "852-12****78"
    },
    "orderAmount": {
      "currency": "HKD",
      "value": "100"
    },
    "orderDescription": "orderDescription001",
    "referenceOrderId": "referenceOrderId001"
  },
  "paymentAmount": {
    "currency": "HKD",
    "value": "100"
  },
  "paymentMethod": {
    "paymentMethodType": "ALIPAY_HK"
  },
  "paymentNotifyUrl": "http://debug1688017773824.test.alipay.net:9090/amsdemo/record/notify?env=main_online&paymentMethodType=ALIPAY_CN",
  "paymentRedirectUrl": "http://debug1688017773824.test.alipay.net:9090/amsdemo/result",
  "paymentRequestId": "paymentRequestId001",
  "productCode": "AGREEMENT_PAYMENT",
  "productScene": "EASY_PAY",
  "settlementStrategy": {
    "settlementCurrency": "USD"
  }
}
响应报文示例:
{
  "paymentSessionData": "ZqeGpu7pbMb/I3dNWTTEL3o4w5mXh20j13VnmsE1p3cjK3CVpnMXY7BfQlIvwNqQWtXHEMUo0R5pQwnSyNtxTA==&&SG&&188&&eyJh***",
  "paymentSessionExpiryTime": "2024-09-27T15:57:30+08:00",
  "paymentSessionId": "ZqeGpu7pbMb/I3dNWTTEL3o4w5mXh20j13VnmsE1p3fI21eGbgq240lFVquZsLrM",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
请根据返回的 result.resultStatus 字段值执行下一步操作:
result.resultStatus
信息
下一步操作
S
表示支付会话创建成功。
将获取到的 paymentSessionData 返回给商户前端。具体操作步骤请参阅步骤 4。
U
表示由于未知原因支付会话创建失败。
请更换 paymentRequestId 和 authstate 重新调用接口以解决问题。如果问题未解决,请联系 Antom 技术支持。
F
表示支付会话创建失败。
请检查并确保接口请求的所有字段(包括请求头和请求体)均已正确传递且有效。
注意:若您未收到响应,可能是网络超时导致,请更换 paymentRequestId 和 authstate 后重试接口调用。
后续支付
public static void createPaymentSession() {
  AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
  alipayPaymentSessionRequest.setProductCode(ProductCodeType.AGREEMENT_PAYMENT);
  alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.EASY_PAY);

  // 替换为您的 paymentRequestId
  String paymentRequestId = UUID.randomUUID().toString();
  alipayPaymentSessionRequest.setPaymentRequestId(paymentRequestId);

  // 设置金额
  // 需进行金额单位转换(实际金额应在服务端计算)
  Amount amount = Amount.builder().currency("HKD").value("98080").build();
  alipayPaymentSessionRequest.setPaymentAmount(amount);

  // 设置结算币种
  SettlementStrategy settlementStrategy = new SettlementStrategy();
  settlementStrategy.setSettlementCurrency("USD");
  alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

  // 设置支付方式
  PaymentMethod paymentMethod = PaymentMethod.builder()
    .paymentMethodType("ALIPAY_HK")
    .paymentMethodId("28288803001319861727421828000Cv96OFlYoi17100****")
    .build();
  alipayPaymentSessionRequest.setPaymentMethod(paymentMethod);

  // 设置买家信息
  Buyer buyer = Buyer.builder().referenceBuyerId("yourBuyerId").build();

  // 替换为您的 orderId
  String orderId = UUID.randomUUID().toString();
  // 设置订单信息
  Order order = Order.builder()
    .referenceOrderId(orderId)
    .orderDescription("antom api testing order")
    .orderAmount(amount)
    .buyer(buyer)
    .build();
  alipayPaymentSessionRequest.setOrder(order);

  // 替换为您的通知地址
  alipayPaymentSessionRequest.setPaymentNotifyUrl("http://www.yourNotifyUrl.com");

  // 替换为您的跳转地址
  alipayPaymentSessionRequest.setPaymentRedirectUrl("http://www.yourRedirectUrl.com");

  AlipayPaymentSessionResponse alipayPaymentSessionResponse;
  try {
    alipayPaymentSessionResponse = CLIENT.execute(alipayPaymentSessionRequest);
  } catch (AlipayApiException e) {
    String errorMsg = e.getMessage();
    // 处理异常情况
  }
}
请求报文示例:
{
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "HKD",
      "value": "98080"
    },
    "orderDescription": "antom api testing order",
    "referenceOrderId": "5e445b58-49ad-4552-a36d-d38f311a090d"
  },
  "paymentAmount": {
    "currency": "HKD",
    "value": "98080"
  },
  "paymentMethod": {
    "paymentMethodId": "28288803001319861727421828000Cv96OFlYoi17100****",
    "paymentMethodType": "ALIPAY_HK"
  },
  "paymentNotifyUrl": "http://www.yourNotifyUrl.com",
  "paymentRedirectUrl": "http://www.yourRedirectUrl.com",
  "paymentRequestId": "5810a84e-3a3e-4e47-bbac-9dfe3f2dd2b3",
  "productCode": "AGREEMENT_PAYMENT",
  "productScene": "EASY_PAY",
  "settlementStrategy": {
    "settlementCurrency": "USD"
  }
}
响应报文示例:
{
  "paymentSessionData": "paymentSessionData****",
  "paymentSessionExpiryTime": "2023-04-06T03:28:49+08:00",
  "paymentSessionId": "paymentSessionId****",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
请根据返回的 result.resultStatus 字段值执行下一步操作:
result.resultStatus
信息
下一步操作
S
表示支付会话创建成功。
将获取到的 paymentSessionData 返回给商户前端。具体操作步骤请参阅步骤 4。
U
表示由于未知原因支付会话创建失败。
请更换 paymentRequestId 重新调用接口以解决问题。如果问题未解决,请联系 Antom 技术支持。
F
表示支付会话创建失败。
请检查并确保接口请求的所有字段(包括请求头和请求体)均已正确传递且有效。
注意:若您未收到响应,可能是网络超时导致,请更换 paymentRequestId 后重试接口调用。
常见问题
问:请求参数的值可以使用中文字符吗?
答:为了避免某些支付方式的不兼容,不要在请求的字段中使用中文字符。
​
问:如何设置接收支付通知的链接?
答:在 createPaymentSession(快捷支付) 接口中指定参数 paymentNotifyUrl,以接收支付结果的异步通知(notifyPayment),或者在 Antom Dashboard 中配置接收链接。如果请求和 Antom Dashboard 中都指定了链接,请求中指定的值优先。
​
问:paymentAmount 和 orderAmount 的区别是什么?
答:paymentAmount 是指支付金额,orderAmount 是指订单金额。实际支付金额以 paymentAmount 为准。
​
问:支付请求中 paymentSessionExpiryTime 超时时间具体指的是什么时间?
答:paymentSessionExpiryTime 表示支付会话创建成功至买家完成支付提交的有效期。买家提交支付后,支付处理超时时间为 10 分钟。因此,买家从会话创建到支付完成的整体时限最长可达 1 小时 10 分钟。
​
问:在响应报文中有哪些重点关注字段?
答:关注响应报文中的以下字段:
  • result.resultStatus:用于判断支付创建会话调用结果。
  • paymentSessionData:加密的支付会话数据。将数据传递给前端用于调用 Antom SDK。
  • paymentSessionExpiryTime:支付会话过期时间。

步骤 3:调用 SDK
客户端

在商户服务端获取到 paymentSessionData 后,可通过商户客户端调用 SDK,将收银台跳转至支付方式签约授权页。买家提交支付请求后,SDK 将自动处理加密通信、风控校验、页面跳转及支付指令执行,实现端到端的支付授权闭环流程。

1. 实例化 SDK

  1. 使用 AMSEasyPay 来创建 SDK 实例。配置对象包括以下参数:
参数名称
是否必填
说明
environment
是
用于指定环境信息。有效值有:
  • sandbox:沙箱环境
  • prod:生产环境
locale
否
用于指定语言信息。根据支付方式所在地区选择合适的值。此参数的有效值如下所示,如果使用了以下列表中未列出的值或者未指定,将使用英语。
  • en_US:英语
  • in_ID:印尼语
  • ms_MY:马来语
  • tl_PH:菲律宾语
  • ko_KR:韩语
  • zh_CN:简体中文
  • zh_HK:繁体中文
onEventCallback
否
回调函数,在 SDK 运行期间发生支付事件时会返回特定事件码。
注意:支付成功后支付结果默认不由此函数返回,而是跳转到您指定的支付成功结果页。但当 notRedirectAfterComplete 为 true 且支付流程是在 SDK 内完成时,系统将不会在支付成功后执行跳转,而是提供事件回调。
以下示例代码展示了如何通过 npm 或 CDN 实例化 SDK:
npm
import { AMSEasyPay } from "@alipay/ams-checkout"; //包管理

const checkoutApp = new AMSEasyPay({
  environment: "sandbox",
  locale: "en_US",
  onEventCallback: ({ code, message }) => {},
});
CDN
const checkoutApp = new window.AMSEasyPay({
  environment: "sandbox",
  locale: "en_US",
  onEventCallback: ({ code, message }) => {},
});
  1. 使用实例对象中的 createComponent 创建一个支付组件,涉及的参数如下:
参数名称
是否必填
描述
sessionData
是
使用 sessionData 参数创建配置对象:将 createPaymentSession(快捷支付) 接口响应中的paymentSessionData 完整数据作为此参数的值。
notRedirectAfterComplete
否
Boolean 类型。有效值为:
  • false:默认值。支付完成后将重定向至您的页面。若该值为空,效果相同。
  • true:支付完成后不会自动跳转,需通过客户端事件代码监听后续流程。
注意:
  • 该参数仅适用于在 SDK 内完成支付的场景。
  • 客户端返回的支付结果事件码仅用于客户端页面的跳转操作参考,交易状态的更新请以服务端 notifyPayment 或 inquiryPayment 接口返回结果为准。
创建支付组件示例:
async function create(sessionData) {
  await checkoutApp.createComponent({
    sessionData: sessionData,
    notRedirectAfterComplete: false, // 默认设为 false,表示支付完成后将重定向至您的页面
  });
}
下图展示 SDK 将收银台跳转至支付方式签约授权页的渲染效果:
Web
从商户收银台页面跳转至支付方式页面
WAP
商户收银台页面拉起半浮层或者跳转到钱包应用程序

2. 处理 SDK 回调事件码

以下是 onEventCallback 返回的事件码及其处理建议:
代码
说明
后续操作
SDK_CALL_URL_SUCCESS
打开支付方式应用或跳转商户页面成功。
无需进一步操作。
SDK_LAUNCH_PAYMENT_APP_ERROR
此事件码代表以下事件之一:
  • 未能成功唤起买家选择的支付方式应用,或跳转支付方式收银台页面失败;
  • 跳转商户页面失败。
建议检查调用 createPaymentSession(快捷支付) 请求时,paymentRedirectUrl 参数是否正确传入。
Web/WAP 场景下通常不会出现跳转异常,如果出现异常,建议您校验重定向链接。在 App 场景下,如果频繁出现跳转异常,请联系 Antom 技术支持,排查跳转或唤端问题。
SDK_PAYMENT_CANCEL
买家取消支付(买家在未提交订单的情况下退出支付页面)。
建议使用在有效期内的 paymentSessionData 重新调用 SDK;如果已过期,则需要重新发起一个 createPaymentSession(快捷支付) 请求。
SDK_PAYMENT_SUCCESSFUL
表示支付方式侧支付处理成功。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。建议将买家重定向至支付结果页面。
注意:仅当支付流程在 SDK 内完成时返回此事件码。
SDK_PAYMENT_FAIL
表示支付方式侧支付失败。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。建议将买家重定向至支付结果页面。
注意:仅当支付流程在 SDK 内完成时返回此事件码。
以下示例代码展示了如何处理回调函数:
function onEventCallback({ code }) {
  switch (code) {
    case "SDK_PAYMENT_CANCEL":
      console.log(
        "买家取消了支付(买家未提交订单就退出了支付页面)。您可以在有效期内使用 paymentSessionData 重新调用 SDK;如果已过期,则需要发起新的 createPaymentSession (EasySafePay) 请求。",
      );
      break;
    case "SDK_CALL_URL_SUCCESS":
      console.log("成功调起支付方式应用或跳转至商户页面。");
      break;
    case "SDK_LAUNCH_PAYMENT_APP_ERROR":
      console.log(
        "跳转至支付方式的收银台页面失败,或跳转至商户页面失败。请检查调用 createPaymentSession (EasySafePay) 请求时 paymentRedirectUrl 参数是否正确传递。Web/WAP 场景很少出现跳转异常,但如遇任何异常,建议验证跳转链接。",
      );
      break;
    case "SDK_PAYMENT_SUCCESSFUL":
      console.log(
        "钱包已成功处理支付。请从 Antom 服务器获取最终支付结果,可通过 inquiryPayment API 或 notifyPayment API 确认结果。建议将买家重定向至支付结果页面。",
      );
      break;
    case "SDK_PAYMENT_FAIL":
      console.log(
        "钱包处理支付失败。请从 Antom 服务器获取最终支付结果,可通过 inquiryPayment API 或 notifyPayment API 确认结果。建议将买家重定向至支付结果页面。",
      );
      break;
    default:
      console.log(code);
  }
}

3. 销毁组件

请在以下场景调用 unmount 方法释放 SDK 组件资源:
// 释放 SDK 组件资源
checkoutApp.unmount();
常见问题
问:一个 AMSEasypay 实例是否可以多次执行 createComponent?
答:不支持,若您要再次发起支付,请创建新的 AMSEasypay 实例。
​
问:首次支付和后续支付都需要调用 SDK 吗?
答:是的。
iOS

用户体验

本 iOS 集成指南旨在帮助您快速实现 App 内电子钱包与网银支付功能的集成,并轻松完成支付界面渲染。
各支付方式在 iOS 端的用户体验存在差异,具体交互体验详见下表:
支付方式类别
支付方式
端类型
首次支付
后续支付
电子钱包
iOS
在商户页面完成支付。
在商户页面完成支付。
iOS
跳转至支付方式应用程序完成支付。
在商户页面完成支付。
网银转账
iOS
在商户页面完成支付。
在商户页面完成支付。
电子钱包
电子钱包
以下是电子钱包类支付方式首次支付和后续支付体验图:
首次支付
首次支付时,买家可选择立即支付或完成授权流程。成功授权后,后续支付将启用免密功能。
在商户页面完成支付
跳转至支付方式应用程序支付
后续支付
后续支付仅需提交订单即可完成免密支付。
网银转账
网银转账
网银转账支付场景下的体验流程如图所示,包含首次支付验证与后续支付简化流程。
首次支付
首次支付时,买家可选择立即支付或完成授权流程。成功授权后,后续支付将启用免密功能。
后续支付
后续支付仅需提交订单即可完成免密支付。

支付流程

首次支付
首次支付流程示意图:
  1. 买家进入商户收银台页面
  2. 创建支付会话请求
    买家确认支付后,调用
    createPaymentSession(快捷支付) 接口获取支付会话。
  3. 调用客户端 SDK
    使用支付会话调用 SDK,SDK 将根据支付方式特性自动采集支付要素、渲染支付界面、处理页面跳转并引导买家完成支付。不同支付方式的交互差异详见用户体验。
  4. 获取授权结果
    当授权成功时,Antom 会通过 notifyAuthorization 接口向您发送异步通知。
  1. 获取支付结果
    可通过以下方式同步支付状态:
后续支付
后续支付流程示意图:
  1. 买家进入商户收银台页面
  2. 创建支付会话请求
    买家确认支付后,调用
    createPaymentSession(快捷支付) 接口获取支付会话。
  3. 调用客户端 SDK
    使用支付会话调用 SDK,SDK 将根据支付方式特性自动采集支付要素、渲染支付界面、处理页面跳转并引导买家完成支付。不同支付方式的交互差异详见用户体验。
  4. 获取支付结果
    可通过以下方式同步支付状态:

集成准备

在您开始集成前,请阅读集成指南及接口概述文档,了解服务端接口的集成步骤及调用接口的注意事项,并确保已完成以下预配置:
  • 已获得 client ID。
  • 已完成密钥配置。
  • 已完成异步通知接收地址的配置。
  • 集成 Antom 服务端 SDK 资源包,并完成接口库安装及请求示例初始化。具体操作请参阅服务端 SDK。
  • 如需使用沙箱环境与生产环境进行联调测试,请提前两个工作日联系 Antom 技术支持申请配置。沙箱环境和生产环境需单独配置。
参阅 iOS 端集成 SDK 资源包文档来集成客户端 SDK 资源包。

集成步骤

请按以下流程开始您的集成:
  1. (可选)预加载
  2. 创建支付会话
  3. 调用 SDK
  4. 获取授权和支付结果

(可选)步骤 1:预加载 SDK
客户端

在加载收银台列表页面时,执行预加载可显著提升收银台页面的渲染性能且无负面性能影响。建议在买家选择支付方式时触发预加载。
请参考以下代码实现预加载:
[AMSEasyPay.shared preload];

步骤 2:创建支付会话
服务端

当买家选择由 Antom 提供的支付方式进行支付时,您需要收集支付请求 ID、订单金额、支付方式、订单描述、支付重定向页面链接和支付结果通知链接等必要信息。
调用 createPaymentSession(快捷支付) 接口发起支付会话创建,并使用响应中返回的 paymentSessionData 初始化 Antom SDK。
首次支付和后续支付调用 createPaymentSession(快捷支付) 接口参数说明:
注意:Express Bank Transfer 的首次支付与后续支付使用相同的传参格式,买家只需在支付页面输入手机号即可完成支付。
类型
字段
是否必传
(首次支付)
是否必传
(后续支付)
描述


基础字段
productCode
是
是
EasySafePay 场景下值为:AGREEMENT_PAYMENT。
productScene
是
是
EasySafePay 场景下值为:EASY_PAY。
paymentRequestId
是
是
商户为识别支付请求而分配的专属 ID。
paymentMethod.paymentMethodType
是
是
授权签约的支付方式。
paymentMethod.paymentMethodId
否
是
传入首次支付后在 notifyAuthorization 接口中获得的accessToken 的值。
settlementStrategy
否
否
该笔支付的结算策略。若业务签约了多个结算币种,需在接口中指定。
paymentAmount.value
是
是
支付金额,需按下单币种的最小单位设置,请参考常见币种的最小单位。
paymentAmount.currency
是
是
符合 ISO 4217 标准的三位币种代码。
paymentSessionExpiryTime
否
否
支付会话过期的特定日期和时间。默认过期时间为会话创建后 1 小时。最大不能超过1小时。例如,如果会话在 2023-7-27T12:00:01+08:30 创建,那么默认会话过期时间为 2023-7-27T13:00:01+08:30。
paymentRedirectUrl
是
是
商户端支付结果页,需根据服务端结果展示,非固定为成功页. 若商户从 Web 或 WAP 端发起支付,则传入 WAP 地址;若商户从 App 端发起支付,则传入 scheme地址。
paymentNotifyUrl
否
否
异步通知地址,必须为HTTPS地址。如果已通过门户配置,则该字段可不传。
授权字段
agreementInfo.authState
是
否
用于发起授权而分配的专属 ID。该参数仅在首次支付时传递,后续支付无需传递此参数。

订单字段



order.orderAmount
是
是
订单金额。
order.referenceOrderId
是
是
商户端订单号。
order.orderDescription
是
是
商户端订单描述。
order.buyer.referenceBuyerId
否
否
传入买家信息以供风险决策。传入 referenceBuyerId、buyerPhoneNo、buyerEmail 其中一个即可。

order.buyer
    • buyerPhoneNo
    • buyerEmail
否
否
买家支付账户。提供支付账户相关信息可有效提升支付成功率并降低买家流失风险。具体格式要求请见下表。
各支付方式买家支付账户传参格式如下:
支付方式
传参格式
建议优先传入手机号。手机号格式如下:
  • 手机号码:
    • 中国香港:852-12****78,注意手机号总长度是 8 位(不包含前缀 852-)。
    • 中国大陆:86-137****5678,注意手机号总长度是 11 位(不包含前缀 86-)。
  • 邮箱:xxx@xxx.com。
62-8569****7288,手机号 8 开头且手机号长度不超过 12 位(不包括前缀 62-)。
TrueMoney
买家需要在支付方式侧认证,不需要传入。
首次支付时传入买家支付账号可自动回填买家账号至支付页面,避免手动输入操作。以下是传入和未传入的体验对比图:
传入买家支付账号
无需输入登录账号。
未传入买家支付账号
需手动输入登录账号。
以下是首次支付和后续支付两种不同场景集成示例:
首次支付
@PostMapping("/payment/createSession")
public ResponseEntity<ApiResponse> createPaymentSession(@RequestBody PaymentVO payment) {
    AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
    alipayPaymentSessionRequest.setProductCode(ProductCodeType.AGREEMENT_PAYMENT);
    alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.EASY_PAY);

    // 替换为您的 paymentRequestId
    String paymentRequestId = UUID.randomUUID().toString();
    alipayPaymentSessionRequest.setPaymentRequestId(paymentRequestId);

    // 转换金额单位(实际使用中,金额应在服务端计算)
    // 详情请参考:<a href="https://docs.antom.com/ac/ref/cc">金额对象使用规则</a>
    long amountMinorLong = Money.of(CurrencyUnit.of(payment.currency), new BigDecimal(payment.amountValue)).getAmountMinorLong();

    // 设置金额
    Amount amount = Amount.builder().currency(payment.currency).value(String.valueOf(amountMinorLong)).build();
    alipayPaymentSessionRequest.setPaymentAmount(amount);

    // 设置结算策略
    // 替换为您现有的结算货币
    SettlementStrategy settlementStrategy = SettlementStrategy.builder().settlementCurrency("USD").build();
    alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

    User loginUser = users.get(payment.getUserId());

    // 设置买家信息
    Buyer buyer = Buyer.builder().referenceBuyerId("yourBuyerId").build();

    // 设置 paymentMethod
    PaymentMethod paymentMethod = PaymentMethod.builder().paymentMethodType(payment.paymentMethodType).build();
    if (loginUser.getPaymentMethodTypeAccessToken().containsKey(payment.getPaymentMethodType())) {
        // 买家已授权
        String accessToken = loginUser.getPaymentMethodTypeAccessToken().get(payment.getPaymentMethodType());
        paymentMethod.setPaymentMethodId(accessToken);
    } else {
        // 设置 agreementInfo
        // 替换为您的 authState
        String authState = UUID.randomUUID().toString();
        AgreementInfo agreementInfo = AgreementInfo.builder().authState(authState).build();
        alipayPaymentSessionRequest.setAgreementInfo(agreementInfo);

        // 保存与 authState 对应的 paymentMethodType
        authStatePayment.put(authState, payment);

        // 买家在支付方式客户端注册时使用的登录 ID,登录 ID 可以是买家的电子邮箱地址或手机号码
        // 指定此参数可免去买家手动输入登录 ID
        if(StringUtil.isNotBlank(loginUser.getPhoneNumber()){
            buyer.setBuyerPhoneNo(loginUser.getPhoneNumber());
        }
        if(StringUtil.isNotBlank(loginUser.getEmail() && "ALIPAY_HK".equals(payment.getPaymentMethodType())){
            buyer.setBuyerPhoneNo(loginUser.getPhoneNumber());
        }
    }
    alipayPaymentSessionRequest.setPaymentMethod(paymentMethod);



    // 替换为您的 orderId
    String orderId = UUID.randomUUID().toString();
    // 设置订单信息
    Order order = Order.builder().referenceOrderId(orderId)
    .orderDescription("antom sdk testing order").orderAmount(amount).buyer(buyer).build();
    alipayPaymentSessionRequest.setOrder(order);

    // 替换为您的通知 URL
    // 或在此配置您的通知 URL:<a href="https://dashboard.antom.com/global-payments/developers/iNotify">通知URL</a>
    alipayPaymentSessionRequest.setPaymentNotifyUrl("http://www.yourNotifyUrl.com/payment/receivePaymentNotify");

    // 替换为您的重定向 URL
    alipayPaymentSessionRequest.setPaymentRedirectUrl(
            "http://localhost:8080/index.html?paymentRequestId=" + paymentRequestId);

    AlipayPaymentSessionResponse alipayPaymentSessionResponse;
    try {
        long startTime = System.currentTimeMillis();
        System.out.println("payment request: " + JSON.toJSONString(alipayPaymentSessionRequest));
        alipayPaymentSessionResponse = CLIENT.execute(alipayPaymentSessionRequest);
        System.out.println("payment response: " + JSON.toJSONString(alipayPaymentSessionResponse));
        System.out.println("payment request cost time: " + (System.currentTimeMillis() - startTime) + "ms");
    } catch (AlipayApiException e) {
        return ResponseEntity.ok().body(new ApiResponse(paymentRequestId, payment.getUserId(), e));
    }
    return ResponseEntity.ok().body(new ApiResponse(paymentRequestId, payment.getUserId(), alipayPaymentSessionResponse));
}
请求报文示例:
{
  "agreementInfo": {
    "authState": "authState001"
  },
  "order": {
    "buyer": {
      "referenceBuyerId": "referenceBuyerId001",
      "buyerPhoneNo": "852-12****78"
    },
    "orderAmount": {
      "currency": "HKD",
      "value": "100"
    },
    "orderDescription": "orderDescription001",
    "referenceOrderId": "referenceOrderId001"
  },
  "paymentAmount": {
    "currency": "HKD",
    "value": "100"
  },
  "paymentMethod": {
    "paymentMethodType": "ALIPAY_HK"
  },
  "paymentNotifyUrl": "http://debug1688017773824.test.alipay.net:9090/amsdemo/record/notify?env=main_online&paymentMethodType=ALIPAY_CN",
  "paymentRedirectUrl": "http://debug1688017773824.test.alipay.net:9090/amsdemo/result",
  "paymentRequestId": "paymentRequestId001",
  "productCode": "AGREEMENT_PAYMENT",
  "productScene": "EASY_PAY",
  "settlementStrategy": {
    "settlementCurrency": "USD"
  }
}
响应报文示例:
{
  "paymentSessionData": "ZqeGpu7pbMb/I3dNWTTEL3o4w5mXh20j13VnmsE1p3cjK3CVpnMXY7BfQlIvwNqQWtXHEMUo0R5pQwnSyNtxTA==&&SG&&188&&eyJh***",
  "paymentSessionExpiryTime": "2024-09-27T15:57:30+08:00",
  "paymentSessionId": "ZqeGpu7pbMb/I3dNWTTEL3o4w5mXh20j13VnmsE1p3fI21eGbgq240lFVquZsLrM",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
请根据返回的 result.resultStatus 字段值执行下一步操作:
result.resultStatus
信息
下一步操作
S
表示支付会话创建成功。
将获取到的 paymentSessionData 返回给商户前端。具体操作步骤请参阅步骤 4。
U
表示由于未知原因支付会话创建失败。
请更换 paymentRequestId 和 authstate 重新调用接口以解决问题。如果问题未解决,请联系 Antom 技术支持。
F
表示支付会话创建失败。
请检查并确保接口请求的所有字段(包括请求头和请求体)均已正确传递且有效。
注意:若您未收到响应,可能是网络超时导致,请更换 paymentRequestId 和 authstate 后重试接口调用。
后续支付
public static void createPaymentSession() {
  AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
  alipayPaymentSessionRequest.setProductCode(ProductCodeType.AGREEMENT_PAYMENT);
  alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.EASY_PAY);

  // 替换为您的 paymentRequestId
  String paymentRequestId = UUID.randomUUID().toString();
  alipayPaymentSessionRequest.setPaymentRequestId(paymentRequestId);

  // 设置金额
  // 需进行金额单位转换(实际金额应在服务端计算)
  Amount amount = Amount.builder().currency("HKD").value("98080").build();
  alipayPaymentSessionRequest.setPaymentAmount(amount);

  // 设置结算币种
  SettlementStrategy settlementStrategy = new SettlementStrategy();
  settlementStrategy.setSettlementCurrency("USD");
  alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

  // 设置支付方式
  PaymentMethod paymentMethod = PaymentMethod.builder()
    .paymentMethodType("ALIPAY_HK")
    .paymentMethodId("28288803001319861727421828000Cv96OFlYoi17100****")
    .build();
  alipayPaymentSessionRequest.setPaymentMethod(paymentMethod);

  // 设置买家信息
  Buyer buyer = Buyer.builder().referenceBuyerId("yourBuyerId").build();

  // 替换为您的 orderId
  String orderId = UUID.randomUUID().toString();
  // 设置订单信息
  Order order = Order.builder()
    .referenceOrderId(orderId)
    .orderDescription("antom api testing order")
    .orderAmount(amount)
    .buyer(buyer)
    .build();
  alipayPaymentSessionRequest.setOrder(order);

  // 替换为您的通知地址
  alipayPaymentSessionRequest.setPaymentNotifyUrl("http://www.yourNotifyUrl.com");

  // 替换为您的跳转地址
  alipayPaymentSessionRequest.setPaymentRedirectUrl("http://www.yourRedirectUrl.com");

  AlipayPaymentSessionResponse alipayPaymentSessionResponse;
  try {
    alipayPaymentSessionResponse = CLIENT.execute(alipayPaymentSessionRequest);
  } catch (AlipayApiException e) {
    String errorMsg = e.getMessage();
    // 处理异常情况
  }
}
请求报文示例:
{
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "HKD",
      "value": "98080"
    },
    "orderDescription": "antom api testing order",
    "referenceOrderId": "5e445b58-49ad-4552-a36d-d38f311a090d"
  },
  "paymentAmount": {
    "currency": "HKD",
    "value": "98080"
  },
  "paymentMethod": {
    "paymentMethodId": "28288803001319861727421828000Cv96OFlYoi17100****",
    "paymentMethodType": "ALIPAY_HK"
  },
  "paymentNotifyUrl": "http://www.yourNotifyUrl.com",
  "paymentRedirectUrl": "http://www.yourRedirectUrl.com",
  "paymentRequestId": "5810a84e-3a3e-4e47-bbac-9dfe3f2dd2b3",
  "productCode": "AGREEMENT_PAYMENT",
  "productScene": "EASY_PAY",
  "settlementStrategy": {
    "settlementCurrency": "USD"
  }
}
响应报文示例:
{
  "paymentSessionData": "paymentSessionData****",
  "paymentSessionExpiryTime": "2023-04-06T03:28:49+08:00",
  "paymentSessionId": "paymentSessionId****",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
请根据返回的 result.resultStatus 字段值执行下一步操作:
result.resultStatus
信息
下一步操作
S
表示支付会话创建成功。
将获取到的 paymentSessionData 返回给商户前端。具体操作步骤请参阅步骤 4。
U
表示由于未知原因支付会话创建失败。
请更换 paymentRequestId 重新调用接口以解决问题。如果问题未解决,请联系 Antom 技术支持。
F
表示支付会话创建失败。
请检查并确保接口请求的所有字段(包括请求头和请求体)均已正确传递且有效。
注意:若您未收到响应,可能是网络超时导致,请更换 paymentRequestId 后重试接口调用。
常见问题
问:请求参数的值可以使用中文字符吗?
答:为了避免某些支付方式的不兼容,不要在请求的字段中使用中文字符。
​
问:如何设置接收支付通知的链接?
答:在 createPaymentSession(快捷支付) 接口中指定参数 paymentNotifyUrl,以接收支付结果的异步通知(notifyPayment),或者在 Antom Dashboard 中配置接收链接。如果请求和 Antom Dashboard 中都指定了链接,请求中指定的值优先。
​
问:paymentAmount 和 orderAmount 的区别是什么?
答:paymentAmount 是指支付金额,orderAmount 是指订单金额。实际支付金额以 paymentAmount 为准。
​
问:支付请求中 paymentSessionExpiryTime 超时时间具体指的是什么时间?
答:paymentSessionExpiryTime 表示支付会话创建成功至买家完成支付提交的有效期。买家提交支付后,支付处理超时时间为 10 分钟。因此,买家从会话创建到支付完成的整体时限最长可达 1 小时 10 分钟。
​
问:在响应报文中有哪些重点关注字段?
答:关注响应报文中的以下字段:
  • result.resultStatus:用于判断支付创建会话调用结果。
  • paymentSessionData:加密的支付会话数据。将数据传递给前端用于调用 Antom SDK。
  • paymentSessionExpiryTime: 支付会话过期时间。

步骤 3:调用 SDK
客户端

在商户服务端获取到 paymentSessionData 后,可通过商户客户端调用 SDK,将收银台跳转至支付方式签约授权页。买家提交支付请求后,SDK 将自动处理加密通信、风控校验、页面跳转及支付指令执行,实现端到端的支付授权闭环流程。

1. 实例化 SDK

  1. 使用 AMSEasyPayConfiguration 来创建 SDK 实例。配置对象包括以下参数:
参数
是否必需
说明
locale
否
用于指定语言信息。根据支付方式的地区选择合适的值。此参数的有效值如下所示,如果使用了以下列表中未列出的值或者未指定,将使用英语。
  • en_US:英语
  • in_ID:印尼语
  • ms_MY:马来语
  • tl_PH:菲律宾语
  • ko_KR:韩语
  • zh_CN:简体中文
  • zh_HK:繁体中文
options
否
用于指定是否使用沙箱环境和支付后跳转。有效值包括:
  • "sandbox", "true":沙箱环境
  • "sandbox", "false":生产环境
  • "notRedirectAfterComplete", "false":选传,Boolean 类型。默认值为 false,表示支付完成后跳回您的页面,当值为空时同理。值为 true 表示支付完成后不跳转,您需要通过客户事件码自行控制绑卡完成后续流程。
注意:
  • 该参数仅适用于在 SDK 内完成支付的场景。
  • 客户端返回的支付结果事件码仅用于客户端页面的跳转操作参考,交易状态的更新请以服务端 notifyPayment 或 inquiryPayment 接口返回结果为准。
fromScheme
否
在接入 Alipay 的场景下,买家在 Alipay 钱包完成支付后,会根据您配置的 exampleForScheme 回跳到商户 App。
注意:在 iOS Native 环境下,Alipay 仅支持回跳至 componentConfig.fromScheme 指定的 App 启动页,而非 createPaymentSession(快捷支付) 接口里指定的 paymentRedirectUrl,需通过事件码控制具体跳转页面。
  1. 创建 AMSPaymentProtocol 接口的实例,用于处理支付回调的结果,包含以下方法:
方法
是否必需
说明
onEventCallback
否
用于监听收银台页面支付事件的回调函数,返回 eventCode 和 eventResult。支付成功结果默认不会从此回调函数返回,而是跳转到您指定的支付成功结果页。但当 notRedirectAfterComplete 为 true 时,系统将不会在支付完成后执行跳转,而是提供事件回调。
注意:当接入 Alipay 时,如果您调用了 processOrderWithPaymentResult方法,将会收到 onEventCallback 返回的支付事件码。如果没有调用 processOrderWithPaymentResult 方法,则不会收到支付事件码,您可以通过调用服务端的 inquiryPayment 接口获取支付结果。
实例化 SDK 示例:
#import <AMSComponent/AMSComponent-Swift.h>

  
AMSEasyPayConfiguration *componentConfig = [AMSEasyPayConfiguration new];
// Alipay 需要添加 fromScheme,该地址用于支付完成后跳回 App 启动页
componentConfig.fromScheme = @"exampleForScheme";

componentConfig.locale = @"en_US";
// 设置沙箱环境,若为空则默认使用生产环境
NSDictionary *options = @{@"sandbox": @"true",
                          @"notRedirectAfterComplete": @"false"};     
componentConfig.options = options;

[[AMSEasyPay shared] initConfiguration:componentConfig];

// 设置回调以监控支付页面的支付事件
[AMSEasyPay shared].paymentDelegate = self;


#pragma AMSPaymentProtocol
- (void)onEventCallback:(NSString *)eventCode eventResult:(AMSEventResult *)eventResult
{
    NSLog(@"eventCode%@ eventResult%@", eventCode, eventResult);
}
下图展示了商户收银台页面拉起半浮层或者跳转到钱包 App 的渲染效果:
  1. 使用实例对象中的 createComponent 创建一个支付组件,其包含的方法如下:
参数名称
是否必填
说明
sessionData
是
使用 sessionData 参数创建配置对象:将 createPaymentSession(快捷支付) 接口响应中的 paymentSessionData 完整数据作为此参数的值。
示例代码如下:
[[AMSEasyPay shared] createComponent:sessionData];

2. 处理 SDK 回调事件码

onEventCallback 返回的事件码,请根据相关处理建议进行下一步操作。
类型
代码
说明
后续操作
状态码
SDK_CALL_URL_SUCCESS
表示 paymentRedirectUrl 重定向成功。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
SDK_PAYMENT_CANCEL
表示买家取消支付,即买家未提交订单退出支付页面。
您可使用有效期内的 paymentSessionData 重新调用 SDK。如已过期,需要重新发起 createPaymentSession(快捷支付) 请求。
SDK_PAYMENT_SUCCESSFUL
表示支付方式侧支付成功。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
注意:对于未接入 Alipay 的支付方案,建议将用户重定向至支付结果页。
SDK_PAYMENT_FAIL
表示支付方式侧支付失败。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
注意:对于未接入 Apipay 的支付方案,建议将用户重定向至支付结果页。
SDK_PAYMENT_PROCESSING
(仅 Alipay 返回)
表示支付宝钱包侧的支付处于未知状态。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
SDK_PAYMENT_ERROR
(仅 Alipay 返回)
表示支付宝钱包对支付处理异常。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
错误码
SDK_CREATEPAYMENT_PARAMETER_ERROR
createComponent 函数传入参数异常。
请检查参数是否正确并重新初始化组件。
SDK_STATUS_ERROR
此事件码代表以下事件信息中的一种情况:
  • 在上一笔支付流程未完成时调用了 createComponent。
  • 未初始化即调用 createComponent。
  • 当前已有弹窗激活时调用 createComponent。
请检查返回结果信息,并匹配对应的错误情况。
SDK_INTEGRATION_ERROR
此事件码表示 SDK 的某些依赖项不存在。当前存在以下两种情况:
  • 安全 SDK 不存在。
  • 安全浏览器打开失败。
请检查目标模块是否集成。
SDK_LAUNCH_PAYMENT_APP_ERROR
此事件码代表以下事件信息中的一种情况:
建议检查调用 createPaymentSession(快捷支付) 请求时,paymentRedirectUrl 参数是否正确传入。
如果频繁出现跳转异常,请联系 Antom 技术支持,排查跳转或唤端问题。
以下示例代码展示了如何处理 onEventCallback 回调函数:
-(void)onEventCallback:(NSString *)eventCode eventResult:(AMSEventResult *)eventResult {
    if ([[_selectMethod title] isEqualToString:@"ALIPAY_CN"]) {
        if ([eventCode isEqualToString:@"SDK_PAYMENT_CANCEL"]) {
            NSLog(@"买家取消支付(未提交订单退出支付页面),有效期内可使用原paymentSessionData重新调用SDK,过期则需重新发起创建支付会话请求");
        } else if ([eventCode isEqualToString:@"SDK_PAYMENT_SUCCESSFUL"]) {
            NSLog(@"支付宝钱包支付成功,请通过Antom服务端获取最终支付结果(查询接口/异步通知)");
        } else if ([eventCode isEqualToString:@"SDK_PAYMENT_FAIL"]) {
            NSLog(@"T支付宝钱包支付失败,请通过Antom服务端获取最终结果(查询接口/异步通知)");
        } else if ([eventCode isEqualToString:@"SDK_PAYMENT_PROCESSING"]) {
            NSLog(@"支付宝钱包支付状态未知,请通过Antom服务端获取最终结果(查询接口/异步通知)");
        } else if ([eventCode isEqualToString:@"SDK_PAYMENT_ERROR"]) {
            NSLog(@"支付宝钱包支付异常,请通过Antom服务端获取最终结果(查询接口/异步通知)");
        } else {
            NSLog(@"eventCode%@ eventResult%@", eventCode, eventResult);
        }
    } else {
        // 需要注意,支付成功不会从这个回调函数通知,而是重定向页面到您指定的成功结果页
        if ([eventCode isEqualToString:@"SDK_PAYMENT_CANCEL"]) {
            NSLog(@"买家取消支付(未提交订单退出支付页面),有效期内可使用原paymentSessionData重新调用SDK,过期则需重新发起创建支付会话请求");
        } else if ([eventCode isEqualToString:@"SDK_CALL_URL_SUCCESS"]) {
            NSLog(@"成功跳转至钱包APP或商户页面");
        } else if ([eventCode isEqualToString:@"SDK_LAUNCH_PAYMENT_APP_ERROR"]) {
            NSLog(@"跳转至钱包APP或商户页面失败");
        } else if ([eventCode isEqualToString:@"SDK_CREATEPAYMENT_PARAMETER_ERROR"]) {
            NSLog(@"输入参数无效,请检查后重试");
        } else {
            NSLog(@"eventCode%@ eventResult%@", eventCode, eventResult);
        }
    }
}

3. 销毁组件

在以下情况调用 onDestroy 释放 SDK 组件资源:
在以下情况可以不调用 onDestroy,SDK 会自动释放部分资源(当 iOS SDK AMSComponents 为 1.33.0 及以上版本时)。
  • 当买家发起多笔支付时,且 initConfiguration 中的参数未发生变更。SDK会在支付结束后自行回收部分资源,以重置到 createComponent 之前的状态。
// 完全回收组件资源
[[AMSEasyPay shared] onDestroy];

4. 接收回调函数通知(接入 Alipay 时)

当您接入 Alipay 时,商户客户端可以接收来自回调函数的通知。收到回调函数通知后,使用 AppDelegate 文件中的 openURL 方法处理 Alipay 返回的结果数据:
  • canProcessOrderWithPaymentResult():用于判断从钱包端回跳至商户端的跳转 URL 是否可用。
  • processOrderWithPaymentResult():用于处理从钱包端到商户端的回跳。
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
     if ([url.scheme isEqualToString:@"exampleForScheme"]) {
        if ([[AMSEasyPay shared] canProcessOrderWithPaymentResult:url]) {
            [[AMSEasyPay shared] processOrderWithPaymentResult:url];
        }
    }
    return YES;
}
常见问题
问:是否一定要处理 SDK 回调事件?
答:Alipay 需根据事件码处理支付结果展示,但其他支付方式非必选,您可通过回调事件实现日志埋点。

问:一个 AMSEasypay 实例是否可以多次执行 createComponent?
答:不支持,若您要再次发起支付,请创建新的 AMSEasypay 实例。
​
问:首次支付和后续支付都需要调用 SDK 吗?
答:是的。
Android

用户体验

本 Android 集成指南将协助您快速实现 App 电子钱包及网银支付功能接入,并完成支付页面渲染。
各支付方式在 Android 端的用户体验存在差异,具体交互体验详见下表:
支付方式类别
支付方式
端类型
首次支付
后续支付
电子钱包
Android
在商户页面完成支付。
在商户页面完成支付。
Android
跳转至支付方式应用程序完成支付。
在商户页面完成支付。
网银转账
Android
在商户页面完成支付。
在商户页面完成支付。
电子钱包
电子钱包
以下是电子钱包类支付方式首次支付和后续支付体验图:
首次支付
首次支付时,买家可选择立即支付或完成授权流程。成功授权后,后续支付将启用免密功能。
在商户页面完成支付
跳转至支付方式应用程序支付
后续支付
后续支付仅需提交订单即可完成免密支付。
网银转账
网银转账
网银转账支付场景下的体验流程如图所示,包含首次支付验证与后续支付简化流程。
首次支付
首次支付时,买家可选择立即支付或完成授权流程。成功授权后,后续支付将启用免密功能。
后续支付
后续支付仅需提交订单即可完成免密支付。

支付流程

首次支付
首次支付流程示意图:
  1. 买家进入商户收银台页面
  2. 创建支付会话请求
    买家确认支付后,调用
    createPaymentSession(快捷支付) 接口获取支付会话。
  3. 调用客户端 SDK
    使用支付会话调用 SDK,SDK 将根据支付方式特性自动采集支付要素、渲染支付界面、处理页面跳转并引导买家完成支付。不同支付方式的交互差异详见用户体验。
  4. 获取授权结果
    当授权成功时,Antom 会通过 notifyAuthorization 接口向您发送异步通知。
  1. 获取支付结果
    可通过以下方式同步支付状态:
后续支付
后续支付流程示意图:
  1. 买家进入商户收银台页面
  2. 创建支付会话请求
    买家确认支付后,调用
    createPaymentSession(快捷支付)接口获取支付会话。
  3. 调用客户端 SDK
    使用支付会话调用 SDK,SDK 将根据支付方式特性自动采集支付要素、渲染支付界面、处理页面跳转并引导买家完成支付。不同支付方式的交互差异详见 用户体验。
  4. 获取支付结果
    可通过以下方式同步支付状态:

集成准备

在您开始集成前,请阅读集成指南及接口概述文档,了解服务端接口的集成步骤及调用接口的注意事项,并确保已完成以下预配置:
  • 已获得 client ID。
  • 已完成密钥配置。
  • 已完成异步通知接收地址的配置。
  • 集成 Antom 服务端 SDK 资源包,并完成接口库安装及请求示例初始化。具体操作请参阅服务端 SDK。
  • 如需使用沙箱环境与生产环境进行联调测试,请提前两个工作日联系 Antom 技术支持申请配置。沙箱环境和生产环境需单独配置。
参阅 Android 端集成 SDK 资源包文档来集成客户端 SDK 资源包。

集成步骤

请按以下流程开始您的集成:
  1. (可选)预加载
  2. 创建支付会话
  3. 调用 SDK
  4. 获取授权和支付结果

(可选)步骤 1:预加载 SDK
客户端

在加载收银台列表页面时,执行预加载可显著提升收银台页面的渲染性能且无负面性能影响。建议在买家选择支付方式时触发预加载。
请参考以下代码实现预加载:
AMSEasyPay.preload(getApplicationContext());

步骤 2:创建支付会话
服务端

当买家选择由 Antom 提供的支付方式进行支付时,您需要收集支付请求 ID、订单金额、支付方式、订单描述、支付重定向页面链接和支付结果通知链接等必要信息。
调用 createPaymentSession(快捷支付) 接口发起支付会话创建,并使用响应中返回的 paymentSessionData 初始化 Antom SDK。
首次支付和后续支付调用 createPaymentSession(快捷支付) 接口参数说明:
注意:Express Bank Transfer 的首次支付与后续支付使用相同的传参格式,买家只需在支付页面输入手机号即可完成支付。
类型
字段
是否必传
(首次支付)
是否必传
(后续支付)
描述


基础字段
productCode
是
是
EasySafePay 场景下值为:AGREEMENT_PAYMENT。
productScene
是
是
EasySafePay 场景下值为:EASY_PAY。
paymentRequestId
是
是
商户为识别支付请求而分配的专属 ID。
paymentMethod.paymentMethodType
是
是
授权签约的支付方式。
paymentMethod.paymentMethodId
否
是
传入首次支付后在 notifyAuthorization 接口中获得的accessToken 的值。
settlementStrategy
否
否
该笔支付的结算策略。若业务签约了多个结算币种,需在接口中指定。
paymentAmount.value
是
是
支付金额,需按下单币种的最小单位设置,请参考常见币种的最小单位。
paymentAmount.currency
是
是
符合 ISO 4217 标准的三位币种代码。
paymentSessionExpiryTime
否
否
支付会话过期的特定日期和时间。默认过期时间为会话创建后 1 小时。最大不能超过1小时。例如,如果会话在 2023-7-27T12:00:01+08:30 创建,那么默认会话过期时间为 2023-7-27T13:00:01+08:30。
paymentRedirectUrl
是
是
商户端支付结果页,需根据服务端结果展示,非固定为成功页. 若商户从 Web 或 WAP 端发起支付,则传入 WAP 地址;若商户从 App 端发起支付,则传入 scheme地址。
paymentNotifyUrl
否
否
异步通知地址,必须为HTTPS地址。如果已通过门户配置,则该字段可不传。
授权字段
agreementInfo.authState
是
否
用于发起授权而分配的专属 ID。该参数仅在首次支付时传递,后续支付无需传递此参数。

订单字段



order.orderAmount
是
是
订单金额。
order.referenceOrderId
是
是
商户端订单号。
order.orderDescription
是
是
商户端订单描述。
order.buyer.referenceBuyerId
否
否
传入买家信息以供风险决策。传入 referenceBuyerId、buyerPhoneNo、buyerEmail 其中一个即可。

order.buyer
    • buyerPhoneNo
    • buyerEmail
否
否
买家支付账户。提供支付账户相关信息可有效提升支付成功率并降低买家流失风险。具体格式要求请见下表。
各支付方式买家支付账户传参格式如下:
支付方式
传参格式
建议优先传入手机号。手机号格式如下:
  • 手机号码:
    • 中国香港:852-12****78,注意手机号总长度是 8 位(不包含前缀 852-)。
    • 中国大陆:86-137****5678,注意手机号总长度是 11 位(不包含前缀 86-)。
  • 邮箱:xxx@xxx.com。
62-8569****7288,手机号 8 开头且手机号长度不超过 12 位(不包括前缀 62-)。
TrueMoney
买家需要在支付方式侧认证,不需要传入。
首次支付时传入买家支付账号可自动回填买家账号至支付页面,避免手动输入操作。以下是传入和未传入的体验对比图:
传入买家支付账号
无需输入登录账号。
未传入买家支付账号
需手动输入登录账号。
以下是首次支付和后续支付两种不同场景集成示例:
首次支付
@PostMapping("/payment/createSession")
public ResponseEntity<ApiResponse> createPaymentSession(@RequestBody PaymentVO payment) {
    AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
    alipayPaymentSessionRequest.setProductCode(ProductCodeType.AGREEMENT_PAYMENT);
    alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.EASY_PAY);

    // 替换为您的 paymentRequestId
    String paymentRequestId = UUID.randomUUID().toString();
    alipayPaymentSessionRequest.setPaymentRequestId(paymentRequestId);

    // 转换金额单位(实际使用中,金额应在服务端计算)
    // 详情请参考:<a href="https://docs.antom.com/ac/ref/cc">金额对象使用规则</a>
    long amountMinorLong = Money.of(CurrencyUnit.of(payment.currency), new BigDecimal(payment.amountValue)).getAmountMinorLong();

    // 设置金额
    Amount amount = Amount.builder().currency(payment.currency).value(String.valueOf(amountMinorLong)).build();
    alipayPaymentSessionRequest.setPaymentAmount(amount);

    // 设置结算策略
    // 替换为您现有的结算货币
    SettlementStrategy settlementStrategy = SettlementStrategy.builder().settlementCurrency("USD").build();
    alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

    User loginUser = users.get(payment.getUserId());

    // 设置买家信息
    Buyer buyer = Buyer.builder().referenceBuyerId("yourBuyerId").build();

    // 设置 paymentMethod
    PaymentMethod paymentMethod = PaymentMethod.builder().paymentMethodType(payment.paymentMethodType).build();
    if (loginUser.getPaymentMethodTypeAccessToken().containsKey(payment.getPaymentMethodType())) {
        // 买家已授权
        String accessToken = loginUser.getPaymentMethodTypeAccessToken().get(payment.getPaymentMethodType());
        paymentMethod.setPaymentMethodId(accessToken);
    } else {
        // 设置 agreementInfo
        // 替换为您的 authState
        String authState = UUID.randomUUID().toString();
        AgreementInfo agreementInfo = AgreementInfo.builder().authState(authState).build();
        alipayPaymentSessionRequest.setAgreementInfo(agreementInfo);

        // 保存与 authState 对应的 paymentMethodType
        authStatePayment.put(authState, payment);

        // 买家在支付方式客户端注册时使用的登录 ID,登录 ID 可以是买家的电子邮箱地址或手机号码
        // 指定此参数可免去买家手动输入登录 ID
        if(StringUtil.isNotBlank(loginUser.getPhoneNumber()){
            buyer.setBuyerPhoneNo(loginUser.getPhoneNumber());
        }
        if(StringUtil.isNotBlank(loginUser.getEmail() && "ALIPAY_HK".equals(payment.getPaymentMethodType())){
            buyer.setBuyerPhoneNo(loginUser.getPhoneNumber());
        }
    }
    alipayPaymentSessionRequest.setPaymentMethod(paymentMethod);



    // 替换为您的 orderId
    String orderId = UUID.randomUUID().toString();
    // 设置订单信息
    Order order = Order.builder().referenceOrderId(orderId)
    .orderDescription("antom sdk testing order").orderAmount(amount).buyer(buyer).build();
    alipayPaymentSessionRequest.setOrder(order);

    // 替换为您的通知 URL
    // 或在此配置您的通知 URL:<a href="https://dashboard.antom.com/global-payments/developers/iNotify">通知URL</a>
    alipayPaymentSessionRequest.setPaymentNotifyUrl("http://www.yourNotifyUrl.com/payment/receivePaymentNotify");

    // 替换为您的重定向 URL
    alipayPaymentSessionRequest.setPaymentRedirectUrl(
            "http://localhost:8080/index.html?paymentRequestId=" + paymentRequestId);

    AlipayPaymentSessionResponse alipayPaymentSessionResponse;
    try {
        long startTime = System.currentTimeMillis();
        System.out.println("payment request: " + JSON.toJSONString(alipayPaymentSessionRequest));
        alipayPaymentSessionResponse = CLIENT.execute(alipayPaymentSessionRequest);
        System.out.println("payment response: " + JSON.toJSONString(alipayPaymentSessionResponse));
        System.out.println("payment request cost time: " + (System.currentTimeMillis() - startTime) + "ms");
    } catch (AlipayApiException e) {
        return ResponseEntity.ok().body(new ApiResponse(paymentRequestId, payment.getUserId(), e));
    }
    return ResponseEntity.ok().body(new ApiResponse(paymentRequestId, payment.getUserId(), alipayPaymentSessionResponse));
}
请求报文示例:
{
  "agreementInfo": {
    "authState": "authState001"
  },
  "order": {
    "buyer": {
      "referenceBuyerId": "referenceBuyerId001",
      "buyerPhoneNo": "852-12****78"
    },
    "orderAmount": {
      "currency": "HKD",
      "value": "100"
    },
    "orderDescription": "orderDescription001",
    "referenceOrderId": "referenceOrderId001"
  },
  "paymentAmount": {
    "currency": "HKD",
    "value": "100"
  },
  "paymentMethod": {
    "paymentMethodType": "ALIPAY_HK"
  },
  "paymentNotifyUrl": "http://debug1688017773824.test.alipay.net:9090/amsdemo/record/notify?env=main_online&paymentMethodType=ALIPAY_CN",
  "paymentRedirectUrl": "http://debug1688017773824.test.alipay.net:9090/amsdemo/result",
  "paymentRequestId": "paymentRequestId001",
  "productCode": "AGREEMENT_PAYMENT",
  "productScene": "EASY_PAY",
  "settlementStrategy": {
    "settlementCurrency": "USD"
  }
}
响应报文示例:
{
  "paymentSessionData": "ZqeGpu7pbMb/I3dNWTTEL3o4w5mXh20j13VnmsE1p3cjK3CVpnMXY7BfQlIvwNqQWtXHEMUo0R5pQwnSyNtxTA==&&SG&&188&&eyJh***",
  "paymentSessionExpiryTime": "2024-09-27T15:57:30+08:00",
  "paymentSessionId": "ZqeGpu7pbMb/I3dNWTTEL3o4w5mXh20j13VnmsE1p3fI21eGbgq240lFVquZsLrM",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
请根据返回的 result.resultStatus 字段值执行下一步操作:
result.resultStatus
信息
下一步操作
S
表示支付会话创建成功。
将获取到的 paymentSessionData 返回给商户前端。具体操作步骤请参阅步骤 4。
U
表示由于未知原因支付会话创建失败。
请更换 paymentRequestId 和 authstate 重新调用接口以解决问题。如果问题未解决,请联系 Antom 技术支持。
F
表示支付会话创建失败。
请检查并确保接口请求的所有字段(包括请求头和请求体)均已正确传递且有效。
注意:若您未收到响应,可能是网络超时导致,请更换 paymentRequestId 和 authstate 后重试接口调用。
后续支付
public static void createPaymentSession() {
  AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
  alipayPaymentSessionRequest.setProductCode(ProductCodeType.AGREEMENT_PAYMENT);
  alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.EASY_PAY);

  // 替换为您的 paymentRequestId
  String paymentRequestId = UUID.randomUUID().toString();
  alipayPaymentSessionRequest.setPaymentRequestId(paymentRequestId);

  // 设置金额
  // 需进行金额单位转换(实际金额应在服务端计算)
  Amount amount = Amount.builder().currency("HKD").value("98080").build();
  alipayPaymentSessionRequest.setPaymentAmount(amount);

  // 设置结算币种
  SettlementStrategy settlementStrategy = new SettlementStrategy();
  settlementStrategy.setSettlementCurrency("USD");
  alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

  // 设置支付方式
  PaymentMethod paymentMethod = PaymentMethod.builder()
    .paymentMethodType("ALIPAY_HK")
    .paymentMethodId("28288803001319861727421828000Cv96OFlYoi17100****")
    .build();
  alipayPaymentSessionRequest.setPaymentMethod(paymentMethod);

  // 设置买家信息
  Buyer buyer = Buyer.builder().referenceBuyerId("yourBuyerId").build();

  // 替换为您的 orderId
  String orderId = UUID.randomUUID().toString();
  // 设置订单信息
  Order order = Order.builder()
    .referenceOrderId(orderId)
    .orderDescription("antom api testing order")
    .orderAmount(amount)
    .buyer(buyer)
    .build();
  alipayPaymentSessionRequest.setOrder(order);

  // 替换为您的通知地址
  alipayPaymentSessionRequest.setPaymentNotifyUrl("http://www.yourNotifyUrl.com");

  // 替换为您的跳转地址
  alipayPaymentSessionRequest.setPaymentRedirectUrl("http://www.yourRedirectUrl.com");

  AlipayPaymentSessionResponse alipayPaymentSessionResponse;
  try {
    alipayPaymentSessionResponse = CLIENT.execute(alipayPaymentSessionRequest);
  } catch (AlipayApiException e) {
    String errorMsg = e.getMessage();
    // 处理异常情况
  }
}
请求报文示例:
{
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "HKD",
      "value": "98080"
    },
    "orderDescription": "antom api testing order",
    "referenceOrderId": "5e445b58-49ad-4552-a36d-d38f311a090d"
  },
  "paymentAmount": {
    "currency": "HKD",
    "value": "98080"
  },
  "paymentMethod": {
    "paymentMethodId": "28288803001319861727421828000Cv96OFlYoi17100****",
    "paymentMethodType": "ALIPAY_HK"
  },
  "paymentNotifyUrl": "http://www.yourNotifyUrl.com",
  "paymentRedirectUrl": "http://www.yourRedirectUrl.com",
  "paymentRequestId": "5810a84e-3a3e-4e47-bbac-9dfe3f2dd2b3",
  "productCode": "AGREEMENT_PAYMENT",
  "productScene": "EASY_PAY",
  "settlementStrategy": {
    "settlementCurrency": "USD"
  }
}
响应报文示例:
{
  "paymentSessionData": "paymentSessionData****",
  "paymentSessionExpiryTime": "2023-04-06T03:28:49+08:00",
  "paymentSessionId": "paymentSessionId****",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
请根据返回的 result.resultStatus 字段值执行下一步操作:
result.resultStatus
信息
下一步操作
S
表示支付会话创建成功。
将获取到的 paymentSessionData 返回给商户前端。具体操作步骤请参阅步骤 4。
U
表示由于未知原因支付会话创建失败。
请更换 paymentRequestId 重新调用接口以解决问题。如果问题未解决,请联系 Antom 技术支持。
F
表示支付会话创建失败。
请检查并确保接口请求的所有字段(包括请求头和请求体)均已正确传递且有效。
注意:若您未收到响应,可能是网络超时导致,请更换 paymentRequestId 后重试接口调用。
常见问题
问:请求参数的值可以使用中文字符吗?
答:为了避免某些支付方式的不兼容,不要在请求的字段中使用中文字符。
​
问:如何设置接收支付通知的链接?
答:在 createPaymentSession(快捷支付)接口中指定参数 paymentNotifyUrl,以接收支付结果的异步通知(notifyPayment),或者在 Antom Dashboard 中配置接收链接。如果请求和 Antom Dashboard 中都指定了链接,请求中指定的值优先。
​
问:paymentAmount 和 orderAmount 的区别是什么?
答:paymentAmount 是指支付金额,orderAmount 是指订单金额。实际支付金额以 paymentAmount 为准。
​
问:支付请求中 paymentSessionExpiryTime 超时时间具体指的是什么时间?
答:paymentSessionExpiryTime 表示支付会话创建成功至买家完成支付提交的有效期。买家提交支付后,支付处理超时时间为 10 分钟。因此,买家从会话创建到支付完成的整体时限最长可达 1 小时 10 分钟。
​
问:在响应报文中有哪些重点关注字段?
答:关注响应报文中的以下字段:
  • result.resultStatus: 用于判断支付创建会话调用结果。
  • paymentSessionData: 加密的支付会话数据。将数据传递给前端用于调用 Antom SDK。
  • paymentSessionExpiryTime: 支付会话过期时间。

步骤 3:调用 SDK
客户端

在商户服务端获取到 paymentSessionData 后,可通过商户客户端调用 SDK,将收银台跳转至支付方式签约授权页。买家提交支付请求后,SDK 将自动处理加密通信、风控校验、页面跳转及支付指令执行,实现端到端的支付授权闭环流程。

1. 实例化 SDK

  1. 使用 AMSEasyPayConfiguration 来创建 SDK 实例。配置对象包括以下参数:
参数
是否必填
说明
setLocale
否
用于指定语言信息。根据支付方式所在地区选择合适的值。此参数的有效值如下所示,如果使用了以下列表中未列出的值或者未指定,将使用英语。
  • "en", "US":英语
  • "in", "ID":印尼语
  • "ms", "MY":马来语
  • "tl", "PH":菲律宾语
  • "ko", "KR":韩语
  • "zh", "CN":简体中文
  • "zh", "HK":繁体中文
setOption
否
用于指定是否使用沙箱环境、窗口对齐模式及支付后跳转。有效值为:
  • "sandbox", "true":沙箱环境。
  • "sandbox", "false":生产环境。
  • "windowGravity", "LANDSCAPE_CENTER":横屏模式下窗口居中。
  • "windowGravity", "LANDSCAPE_RIGHT":横屏模式下窗口靠右对齐。
  • "notRedirectAfterComplete", "false":选传,Boolean 类型。默认值为 false,表示支付完成后跳回您的页面,当值为空时同理。值为 true 表示支付完成后不跳转,您需要通过客户事件码自行控制绑卡完成后续流程。
注意:
  • 该参数仅适用于在 SDK 内完成支付的场景。
  • 客户端返回的支付结果事件码仅用于客户端页面的跳转操作参考,交易状态的更新请以服务端 notifyPayment 或 inquiryPayment 接口返回结果为准。
  1. 创建 setOnCheckoutListener 接口的实例,用于处理后续流程中发生的对应事件,包含以下方法:
方法
是否必需
说明
onEventCallback
否
用于监听收银台页面支付事件的回调函数,返回 eventCode 和 eventResult。支付成功结果默认不会从此回调函数返回,而是跳转到您指定的支付成功结果页。但当 notRedirectAfterComplete 为 true 时,系统将不会在支付完成后执行跳转,而是提供事件回调。
注意:当接入 Alipay 时,您将收到 onEventCallback 返回的支付事件码。
实例化 SDK 示例:
// 步骤一: 创建 AMSEasyPayConfiguration 类。
AMSEasyPayConfiguration configuration = new AMSEasyPayConfiguration();
configuration.setLocale(new Locale("en", "US"));
// 设置沙箱环境。如果将其置空,则默认使用线上正式环境。
configuration.setOption("sandbox", "true");
// 默认设置为 false
configuration.setOption("notRedirectAfterComplete", "false");
// 设置回调来监听收银台页面的支付事件。
configuration.setOnCheckoutListener(
  new OnCheckoutListener() {
    @Override
    public void onEventCallback(String eventCode, AMSEventResult eventResult) {
      // eventCode 请参考本文末的示例代码或事件码列表。
      Toast.makeText(
        activity,
        "eventCode=" + eventCode + " message=" + message,
        Toast.LENGTH_SHORT
      ).show();
    }
  }
);

// 实例化 AMSEasyPay。
AMSEasyPay checkout = new AMSEasyPay.Builder(activity, configuration).build();
下图展示了商户收银台页面拉起半浮层或者跳转到钱包应用程序的渲染效果:
  1. 使用实例对象中的createComponent创建一个支付组件,其包含的方法如下:
参数名称
是否必填
说明
sessionData
是
使用 sessionData 参数创建配置对象:将 createPaymentSession(快捷支付) 接口响应中的 paymentSessionData 完整数据作为此参数的值。
示例代码如下:
checkout.createComponent(activity, sessionData);

2. 处理 SDK 回调事件码

onEventCallback 返回的事件码,请根据相关处理建议进行下一步操作。
注意:在 Android Native 环境下,Alipay 仅支持回跳至商户 App 启动页,而非 createPaymentSession(快捷支付) 接口里指定的 paymentRedirectUrl,需通过事件码控制具体跳转页面。
类型
代码
说明
后续操作
状态码
SDK_CALL_URL_SUCCESS
表示 paymentRedirectUrl 重定向成功。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
SDK_PAYMENT_CANCEL
表示买家取消支付,即买家未提交订单退出支付页面。
您可使用有效期内的 paymentSessionData 重新调用 SDK。如已过期,需要重新发起 createPaymentSession(快捷支付) 请求。
SDK_PAYMENT_SUCCESSFUL
表示支付方式侧支付成功。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
注意:对于未接入 Alipay 的支付方案,建议将用户重定向至支付结果页。
SDK_PAYMENT_FAIL
表示支付方式侧支付失败。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
注意:对于未接入 Alipay 的支付方案,建议将用户重定向至支付结果页。
SDK_PAYMENT_PROCESSING
(仅 Alipay 返回)
表示支付宝钱包侧的支付处于未知状态。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
SDK_PAYMENT_ERROR
(仅 Alipay 返回)
表示支付宝钱包对支付处理异常。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
错误码
​
SDK_INTERNAL_ERROR
SDK 内部错误。
请联系 Antom 技术支持。
SDK_CREATEPAYMENT_PARAMETER_ERROR
createComponent 函数传入参数异常。
请检查参数是否正确并重新初始化组件。
SDK_STATUS_ERROR
此事件码代表以下事件信息中的一种情况:
  • 在上一笔支付流程未完成时调用了 createComponent。
  • 未初始化即调用 createComponent。
  • 当前已有弹窗激活时调用 createComponent。
请检查返回结果信息,并匹配对应的错误情况。
SDK_INTEGRATION_ERROR
此事件码表示 SDK 的某些依赖项不存在。当前存在以下两种情况:
  • 安全 SDK 不存在。
  • 安全浏览器打开失败。
请检查目标模块是否集成。
SDK_LAUNCH_PAYMENT_APP_ERROR
此事件码代表以下事件信息中的一种情况:
建议检查调用 createPaymentSession(快捷支付) 请求时,paymentRedirectUrl 参数是否正确传入。
如果频繁出现跳转异常,请联系 Antom 技术支持,排查跳转或唤端问题。
以下示例代码展示了如何处理 onEventCallback 回调函数:
configuration.setOnCheckoutListener(
  new OnCheckoutListener() {
    @Override
    public void onEventCallback(String eventCode, AMSEventResult eventResult) {
      if (selectPayment != null && selectPayment.getPaymentMethodCode().equals("ALIPAY_CN")) {
        switch (eventCode) {
          case "SDK_PAYMENT_CANCEL":
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "买家已取消支付(买家未提交订单即退出支付页面)。有效期内可使用原paymentSessionData重新调起SDK;若已过期需重新发起createPaymentSession(EasySafePay)请求"
            );
            break;
          case "SDK_PAYMENT_SUCCESSFUL":
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "支付宝钱包处理支付成功,请通过Antom服务器获取最终支付结果。可通过inquiryPayment接口或notifyPayment接口确认"
            );
            break;
          case "SDK_PAYMENT_FAIL":
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "支付宝钱包处理支付失败,请通过Antom服务器获取最终支付结果。可通过inquiryPayment接口或notifyPayment接口确认"
            );
            break;
          case "SDK_PAYMENT_PROCESSING":
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "支付宝钱包支付状态未知,请通过Antom服务器获取最终支付结果。可通过inquiryPayment接口或notifyPayment接口确认"
            );
            break;
          case "SDK_PAYMENT_ERROR":
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "支付宝钱包支付处理异常,请通过Antom服务器获取最终支付结果。可通过inquiryPayment接口或notifyPayment接口确认"
            );
            break;
          default:
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "eventCode=" + eventCode + " message=" + eventResult.getMessage()
            );
            break;
        }
      } else {
        // 需要注意,支付成功不会从这个回调函数通知,而是重定向页面到您指定的成功结果页
        switch (eventCode) {
          case "SDK_PAYMENT_CANCEL":
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "买家已取消支付(买家未提交订单即退出支付页面)。有效期内可使用原paymentSessionData重新调起SDK;若已过期需重新发起createPaymentSession请求"
            );
            break;
          case "SDK_CALL_URL_SUCCESS":
            AlertUtils.showAlertWithMessage(MainActivity.this, "成功打开钱包应用或跳转至商户页面");
            break;
          case "SDK_LAUNCH_PAYMENT_APP_ERROR":
            AlertUtils.showAlertWithMessage(MainActivity.this, "打开钱包应用或跳转至商户页面失败");
            break;
          case "SDK_PAYMENT_SUCCESSFUL":
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "钱包处理支付成功,请通过Antom服务器获取最终支付结果。可通过inquiryPayment接口或notifyPayment接口确认。建议将买家重定向至支付结果页"
            );
            break;
          case "SDK_PAYMENT_FAIL":
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "钱包处理支付失败,请通过Antom服务器获取最终支付结果。可通过inquiryPayment接口或notifyPayment接口确认。建议将买家重定向至支付结果页"
            );
            break;
          case "SDK_STATUS_ERROR":
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "支付流程状态异常,请检查是否按正确步骤调用createComponent"
            );
          case "SDK_INTEGRATION_ERROR":
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "SDK依赖项不存在,请检查目标模块是否已集成"
            );
            break;
          case "SDK_CREATEPAYMENT_PARAMETER_ERROR":
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "提供的输入参数无效,请检查参数值后重试"
            );
            break;
          default:
            AlertUtils.showAlertWithMessage(
              MainActivity.this,
              "eventCode=" + eventCode + " message=" + eventResult.getMessage()
            );
            break;
        }
      }
    }
  }
);

3. 销毁组件

在以下情况调用 onDestroy 释放 SDK 组件资源:
在以下情况可以不调用 onDestroy,SDK 会自动释放部分资源(当 SDK 为 1.33.0 及以上版本时)。
  • 当买家发起多笔支付时,且 AMSEasyPayConfiguration 中的参数未发生变更。SDK 会在支付结束后自行回收部分资源,以重置到 createComponent 之前的状态。
以下示例代码展示了如何回收组件:
// 完全回收 SDK 组件资源
checkout.onDestroy();
常见问题
问:是否一定要处理 SDK 回调事件?
答:Alipay 需根据事件码处理支付结果展示,但其他支付方式非必选,您可通过回调事件实现日志埋点。

问:一个 AMSEasypay 实例是否可以多次执行 createComponent?
答:不支持,若您要再次发起支付,请创建新的 AMSEasypay 实例。
​
问:首次支付和后续支付都需要调用 SDK 吗?
答:是的。
WebView

用户体验

以下分别为电子钱包和网银转账两种支付类别的用户体验图:
电子钱包
电子钱包支付场景下,系统将引导买家在商户页面或支付方式应用程序内完成交易。首次支付验证与后续支付简化流程如图所示:
首次支付
首次支付时,买家可以选择直接支付或者授权流程。当用户完成授权后,后续支付可以使用免密支付能力。
商户页面完成支付
跳转到支付方式页面完成支付
后续支付
买家仅需提交订单即可完成免密支付。
网银转账
网银转账支付场景下的体验流程如图所示,包含首次支付验证与后续支付简化流程。
首次支付
在首次支付时,买家完成支付授权流程以确保后续免密支付。
后续支付
后续无需验证码即可完成支付。

支付流程

以下是首次支付和后续支付流程示意图:
首次支付
首次支付流程示意图
  1. 买家进入商户收银台页面
  2. 当买家选择支付方式后,预加载Antom SDK
  3. 创建支付会话请求
    在买家点击支付按钮后,您可以通过调用 createPaymentSession(快捷支付) 接口来获取支付会话。
  4. 调用客户端 SDK
    在客户端通过支付会话调用 SDK。SDK 将会基于支付方式的特点收集支付要素、展示支付页面信息、进行重定向等,并引导买家完成支付。不同支付方式差异请参考支付方式特性-体验差异处理。
  5. 获取授权结果
当授权成功时,Antom 会通过 notifyAuthorization 接口向您发送异步通知。
  1. 获取支付结果
    通过以下两种方法获取支付结果:
后续支付
后续支付流程示意图
  1. 买家进入商户收银台页面
  2. 当买家选择支付方式后,预加载Antom SDK
  3. 创建支付会话请求
    在买家选择支付方式并提交订单后,您可以通过调用 createPaymentSession(快捷支付) 接口来获取支付会话。
  4. 调用客户端 SDK
    在客户端通过支付会话调用 SDK。SDK 会基于支付方式的特点直接完成支付或者收集支付要素、重定向等,直至买家完成支付。不同支付方式差异请参考支付方式特性-体验差异处理。
  5. 获取支付结果
    通过以下两种方法获取支付结果:

集成准备

在您开始集成前,请阅读集成指南及接口概述文档,了解服务端接口的集成步骤及调用接口的注意事项,并确保已完成以下预配置:
  • 已获得 client ID。
  • 已完成密钥配置。
  • 已完成异步通知接收地址的配置。
  • 集成 Antom 服务端 SDK 资源包,并完成接口库安装及请求示例初始化。具体操作请参阅服务端 SDK。
  • 如需使用沙箱环境与生产环境进行联调测试,请提前两个工作日联系 Antom 技术支持申请配置。沙箱环境和生产环境需单独配置。
版本要求
  • iOS 版本要求:
    • 安装 Xcode 12 或更高版本。
    • 使用 iOS 11 或更高版本。
  • Android 版本要求:使用 Android 4.4(API level 19) 或更高版本。
注意:暂不支持 ‌Flutter 和 React Native(RN)开发框架。
参阅 Web/WAP 端集成 SDK 资源包文档来集成客户端 SDK 资源包。

集成步骤

通过以下步骤开始您的集成:
  1. (可选)预加载
  2. 创建支付会话
  3. 调用SDK
  4. 获取授权和支付结果

(可选)步骤 1:预加载 SDK
客户端

在加载收银台列表页面时,执行预加载可显著提升收银台页面的渲染性能且无负面性能影响。建议在买家选择支付方式时触发预加载。
请参考以下代码实现预加载:
AMSEasypay.preload();

步骤 2:创建支付会话
服务端

调用 createPaymentSession(快捷支付) 接口发起支付会话创建,并使用响应中返回的 paymentSessionData 初始化 Antom SDK。
首次支付和后续支付调用接口参数说明:
注意:Express Bank Transfer 的首次支付与后续支付使用相同的传参格式,买家只需在支付页面输入手机号即可完成支付。
类型
字段
是否必传
(首次支付)
是否必传
(后续支付)
字段描述
​
​
基础字段
productCode
是
是
EasySafePay 场景下设置为AGREEMENT_PAYMENT。
productScene
是
是
EasySafePay 场景下设置为 EASY_PAY。
paymentRequestId
是
是
商户为识别支付请求而分配的专属 ID。
paymentMethod.paymentMethodType
是
是
授权签约的支付方式。具体值请参考支付方式枚举值文档。
paymentMethod.paymentMethodId
不涉及
是
传递首次支付后在 notifyAuthorization 接口中获得的 accessToken 的值。
settlementStrategy
否
否
该笔支付的结算策略。若业务签约了多个结算币种,需在接口中指定。若业务只签约了 1 个结算币种,则不需要在接口里指定。
paymentAmount.value
是
是
支付金额,需按下单币种的最小单位设置,如 CNY 为分、KRW 为元。常用币种最小单位请参考币种的最小单位。
paymentSessionExpiryTime
否
否
支付会话过期的特定日期和时间。默认过期时间为会话创建后 1 小时。最大不能超过 1 小时。例如,如果会话在 2023-7-27T12:00:01+08:30 创建,那么默认会话过期时间为 2023-7-27T13:00:01+08:30。
paymentRedirectUrl
是
是
商户端支付结果页,需根据服务端结果展示,非固定为成功页. 若商户从 PC 或 H5 端发起支付,则传入 H5 地址;若商户从App 端发起支付,则传入 scheme 地址。
paymentNotifyUrl
否
否
异步通知地址,如通过门户配置,则该字段可不传,必须为HTTPS 地址。
授权字段
agreementInfo.authState
是
不涉及
您用于发起授权而分配的专属 ID。该参数仅在首次支付时传递,后续支付无需传递此参数。
​
订单字段

​
​
order.orderAmount
是
是
订单金额。
order.referenceOrderId​
是
是
商户端订单号。
order.orderDescription​
是
是
商户端订单描述。
order.buyer.referenceBuyerId
否
否
传递买家信息以供风险决策。传入 referenceBuyerId、buyerPhoneNo、buyerEmail 其中一个参数即可。
​
order.buyer
    • buyerPhoneNo
    • buyerEmail
否
否
买家支付账户。提供支付账户相关信息可有效提升支付成功率并降低买家流失风险。具体格式要求请见下表。
各支付方式买家支付账户传参格式如下:
支付方式
传参格式
建议优先传入手机号。手机号格式如下:
  • 手机号码:
    • 中国香港:852-12****78,注意手机号总长度是 8 位(不包含前缀 852-)。
    • 中国大陆:86-137****5678,注意手机号总长度是 11 位(不包含前缀 86-)。
  • 邮箱:xxx@xxx.com。
62-8569****7288,手机号 8 开头且手机号长度不超过 12 位(不包括前缀 62-)。
TrueMoney
买家需要在支付方式侧认证,不需要传入。
首次支付和后续支付两种不同场景集成代码示例:
首次支付
@PostMapping("/payment/createSession")
public ResponseEntity<ApiResponse> createPaymentSession(@RequestBody PaymentVO payment) {
    AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
    alipayPaymentSessionRequest.setProductCode(ProductCodeType.AGREEMENT_PAYMENT);
    alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.EASY_PAY);

    // 替换为您的 paymentRequestId
    String paymentRequestId = UUID.randomUUID().toString();
    alipayPaymentSessionRequest.setPaymentRequestId(paymentRequestId);

    // 转换金额单位(实际使用中,金额应在服务端计算)
    // 详情请参考:<a href="https://docs.antom.com/ac/ref/cc">金额对象使用规则</a>
    long amountMinorLong = Money.of(CurrencyUnit.of(payment.currency), new BigDecimal(payment.amountValue)).getAmountMinorLong();

    // 设置金额
    Amount amount = Amount.builder().currency(payment.currency).value(String.valueOf(amountMinorLong)).build();
    alipayPaymentSessionRequest.setPaymentAmount(amount);

    // 设置结算策略
    // 替换为您现有的结算货币
    SettlementStrategy settlementStrategy = SettlementStrategy.builder().settlementCurrency("USD").build();
    alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

    User loginUser = users.get(payment.getUserId());

    // 设置买家信息
    Buyer buyer = Buyer.builder().referenceBuyerId("yourBuyerId").build();

    // 设置 paymentMethod
    PaymentMethod paymentMethod = PaymentMethod.builder().paymentMethodType(payment.paymentMethodType).build();
    if (loginUser.getPaymentMethodTypeAccessToken().containsKey(payment.getPaymentMethodType())) {
        // 买家已授权
        String accessToken = loginUser.getPaymentMethodTypeAccessToken().get(payment.getPaymentMethodType());
        paymentMethod.setPaymentMethodId(accessToken);
    } else {
        // 设置 agreementInfo
        // 替换为您的 authState
        String authState = UUID.randomUUID().toString();
        AgreementInfo agreementInfo = AgreementInfo.builder().authState(authState).build();
        alipayPaymentSessionRequest.setAgreementInfo(agreementInfo);

        // 保存与 authState 对应的 paymentMethodType
        authStatePayment.put(authState, payment);

        // 买家在支付方式客户端注册时使用的登录 ID,登录 ID 可以是买家的电子邮箱地址或手机号码
        // 指定此参数可免去买家手动输入登录 ID
        if(StringUtil.isNotBlank(loginUser.getPhoneNumber()){
            buyer.setBuyerPhoneNo(loginUser.getPhoneNumber());
        }
        if(StringUtil.isNotBlank(loginUser.getEmail() && "ALIPAY_HK".equals(payment.getPaymentMethodType())){
            buyer.setBuyerPhoneNo(loginUser.getPhoneNumber());
        }
    }
    alipayPaymentSessionRequest.setPaymentMethod(paymentMethod);



    // 替换为您的 orderId
    String orderId = UUID.randomUUID().toString();
    // 设置订单信息
    Order order = Order.builder().referenceOrderId(orderId)
    .orderDescription("antom sdk testing order").orderAmount(amount).buyer(buyer).build();
    alipayPaymentSessionRequest.setOrder(order);

    // 替换为您的通知 URL
    // 或在此配置您的通知 URL:<a href="https://dashboard.antom.com/global-payments/developers/iNotify">通知URL</a>
    alipayPaymentSessionRequest.setPaymentNotifyUrl("http://www.yourNotifyUrl.com/payment/receivePaymentNotify");

    // 替换为您的重定向 URL
    alipayPaymentSessionRequest.setPaymentRedirectUrl(
            "http://localhost:8080/index.html?paymentRequestId=" + paymentRequestId);

    AlipayPaymentSessionResponse alipayPaymentSessionResponse;
    try {
        long startTime = System.currentTimeMillis();
        System.out.println("payment request: " + JSON.toJSONString(alipayPaymentSessionRequest));
        alipayPaymentSessionResponse = CLIENT.execute(alipayPaymentSessionRequest);
        System.out.println("payment response: " + JSON.toJSONString(alipayPaymentSessionResponse));
        System.out.println("payment request cost time: " + (System.currentTimeMillis() - startTime) + "ms");
    } catch (AlipayApiException e) {
        return ResponseEntity.ok().body(new ApiResponse(paymentRequestId, payment.getUserId(), e));
    }
    return ResponseEntity.ok().body(new ApiResponse(paymentRequestId, payment.getUserId(), alipayPaymentSessionResponse));
}
请求报文示例:
{
  "agreementInfo": {
    "authState": "authState001"
  },
  "order": {
    "buyer": {
      "referenceBuyerId": "referenceBuyerId001",
      "buyerPhoneNo": "852-12****78"
    },
    "orderAmount": {
      "currency": "HKD",
      "value": "100"
    },
    "orderDescription": "orderDescription001",
    "referenceOrderId": "referenceOrderId001"
  },
  "paymentAmount": {
    "currency": "HKD",
    "value": "100"
  },
  "paymentMethod": {
    "paymentMethodType": "ALIPAY_HK"
  },
  "paymentNotifyUrl": "http://debug1688017773824.test.alipay.net:9090/amsdemo/record/notify?env=main_online&paymentMethodType=ALIPAY_CN",
  "paymentRedirectUrl": "http://debug1688017773824.test.alipay.net:9090/amsdemo/result",
  "paymentRequestId": "paymentRequestId001",
  "productCode": "AGREEMENT_PAYMENT",
  "productScene": "EASY_PAY",
  "settlementStrategy": {
    "settlementCurrency": "USD"
  }
}
响应报文示例,包含以下重点参数:
  • paymentSessionData:加密的支付会话数据。将数据传递给前端用于调用 Antom SDK。
  • paymentSessionExpiryTime:支付会话的过期时间。
{
  "paymentSessionData": "paymentSessionData****",
  "paymentSessionExpiryTime": "2023-04-06T03:28:49+08:00",
  "paymentSessionId": "paymentSessionId****",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
请根据返回的 result.resultStatus 字段值执行下一步操作:
result.resultStatus
信息
下一步操作
S
表示支付会话创建成功。
将获取到的 paymentSessionData 返回给商户前端。
U
表示由于未知原因支付会话创建失败。
请更换 paymentRequestId 和 authstate 重新调用接口以解决问题。如果问题未解决,请联系 Antom 技术支持。
F
表示支付会话创建失败。
请检查并确保接口请求的所有字段(包括请求头和请求体)均已正确传递且有效。
注意:若您未收到响应,可能是网络超时导致,请更换 paymentRequestId 和 authstate 后重试接口调用。
后续支付
public static void createPaymentSession() {
  AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
  alipayPaymentSessionRequest.setProductCode(ProductCodeType.AGREEMENT_PAYMENT);
  alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.EASY_PAY);

  // 替换为您的 paymentRequestId
  String paymentRequestId = UUID.randomUUID().toString();
  alipayPaymentSessionRequest.setPaymentRequestId(paymentRequestId);

  // 设置金额
  // 转换金额单位(实际使用中,金额应在服务端计算)
  Amount amount = Amount.builder().currency("HKD").value("98080").build();
  alipayPaymentSessionRequest.setPaymentAmount(amount);

  // 设置结算币种
  SettlementStrategy settlementStrategy = new SettlementStrategy();
  settlementStrategy.setSettlementCurrency("USD");
  alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

  // 设置 paymentMethod
  PaymentMethod paymentMethod = PaymentMethod.builder()
    .paymentMethodType("ALIPAY_HK")
    .paymentMethodId("28288803001319861727421828000Cv96OFlYoi17100****")
    .build();
  alipayPaymentSessionRequest.setPaymentMethod(paymentMethod);

  // 设置买家信息
  Buyer buyer = Buyer.builder().referenceBuyerId("yourBuyerId").build();

  // 替换为您的 orderId
  String orderId = UUID.randomUUID().toString();
  // 设置订单信息
  Order order = Order.builder()
    .referenceOrderId(orderId)
    .orderDescription("antom api testing order")
    .orderAmount(amount)
    .buyer(buyer)
    .build();
  alipayPaymentSessionRequest.setOrder(order);

  // 替换为您的通知 URL
  alipayPaymentSessionRequest.setPaymentNotifyUrl("http://www.yourNotifyUrl.com");

  // 替换为您的重定向 URL
  alipayPaymentSessionRequest.setPaymentRedirectUrl("http://www.yourRedirectUrl.com");

  AlipayPaymentSessionResponse alipayPaymentSessionResponse;
  try {
    alipayPaymentSessionResponse = CLIENT.execute(alipayPaymentSessionRequest);
  } catch (AlipayApiException e) {
    String errorMsg = e.getMessage();
    // 处理错误情况
  }
}
请求报文示例:
{
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "HKD",
      "value": "98080"
    },
    "orderDescription": "antom api testing order",
    "referenceOrderId": "5e445b58-49ad-4552-a36d-d38f311****"
  },
  "paymentAmount": {
    "currency": "HKD",
    "value": "98080"
  },
  "paymentMethod": {
    "paymentMethodId": "28288803001319861727421828000Cv96OFlYoi17100****",
    "paymentMethodType": "ALIPAY_HK"
  },
  "paymentNotifyUrl": "http://www.yourNotifyUrl.com",
  "paymentRedirectUrl": "http://www.yourRedirectUrl.com",
  "paymentRequestId": "5810a84e-3a3e-4e47-bbac-9dfe3f2d****",
  "productCode": "AGREEMENT_PAYMENT",
  "productScene": "EASY_PAY",
  "settlementStrategy": {
    "settlementCurrency": "USD"
  }
}
响应报文示例,包含以下重点参数:
  • paymentSessionData:加密的支付会话数据。将数据传递给前端用于调用 Antom SDK。
  • paymentSessionExpiryTime:支付会话的过期时间。
{
  "paymentSessionData": "paymentSessionData****",
  "paymentSessionExpiryTime": "2023-04-06T03:28:49+08:00",
  "paymentSessionId": "paymentSessionId****",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
请根据返回的 result.resultStatus 字段值执行下一步操作:
result.resultStatus
信息
下一步操作
S
表示支付会话创建成功。
将获取到的 paymentSessionData 返回给商户前端。
U
表示由于未知原因支付会话创建失败。
请更换 paymentRequestId 重新调用接口以解决问题。如果问题未解决,请联系 Antom 技术支持。
F
表示支付会话创建失败。
请检查并确保接口请求的所有字段(包括请求头和请求体)均已正确传递且有效。
注意:若您未收到响应,可能是网络超时导致,请更换 paymentRequestId 后重试接口调用。
常见问题
问:请求参数的值可以使用中文字符吗?
答:为了避免某些支付方式的不兼容,不要在请求的字段中使用中文字符。
​
问:如何设置接收支付通知的链接?
答:在 createPaymentSession(快捷支付) 接口中指定参数 paymentNotifyUrl,以接收支付结果的异步通知(notifyPayment),或者在 Antom Dashboard 中配置接收链接。如果请求和 Antom Dashboard 中都指定了链接,请求中指定的值优先。
​
问:paymentAmount 和 orderAmount 的区别是什么?
答:paymentAmount 是指支付金额,orderAmount 是指订单金额。实际支付金额以 paymentAmount 为准。
​
问:支付请求中 paymentSessionExpiryTime 超时时间具体指的是什么时间?
答:paymentSessionExpiryTime表 示支付会话创建成功至买家完成支付提交的有效期。买家提交支付后,支付处理超时时间为 10 分钟。因此,买家从会话创建到支付完成的整体时限最长可达 1 小时 10 分钟。

步骤 3:调用 SDK
客户端

在商户服务端获取到 paymentSessionData 后,可通过商户客户端调用 SDK,将收银台跳转至支付方式签约授权页。买家提交支付请求后,SDK 将自动处理加密通信、风控校验、页面跳转及支付指令执行,实现端到端的支付授权闭环流程。

1. 实例化 SDK

  1. 使用 AMSEasyPay 来创建 SDK 实例。配置对象包括以下参数:
参数名称
是否必填
说明
environment
是
用于指定环境信息。有效值有:
  • sandbox:沙箱环境
  • prod:生产环境
locale
否
用于指定语言信息。根据支付方式所在地区选择合适的值。此参数的有效值如下所示,如果使用了以下列表中未列出的值或者未指定,将使用英语。
  • en_US:英语
  • in_ID:印尼语
  • ms_MY:马来语
  • tl_PH:菲律宾语
  • ko_KR:韩语
  • zh_CN:简体中文
  • zh_HK:繁体中文
onEventCallback
否
一个回调函数,用于监听结账页面的支付事件,返回 eventCode 和 eventResult。
支付过程中,需设置 isNativeAppWebview 为 true,并额外监听以下事件:
  • SDK_REDIRECT:需要跳转第三方支付页面时触发,eventResult.redirectUrls 包含跳转信息。
支付完成后:
  • 当 notRedirectAfterComplete 为 true 且在 SDK 内完成支付时,支付结果会触发回调。支付成功时返回 SDK_PAYMENT_SUCCESSFUL,支付失败时返回 SDK_PAYMENT_FAIL。若跳转到支付方式支付,则不会有事件码返回。
  • 当 notRedirectAfterComplete 为 false 时,支付结果不会触发回调,而是直接加载您指定的结果页,即您在支付接口里指定的 paymentRedirectUrl。
注意:事件码只能用于处理前端跳转,支付结果需要依赖服务端的支付结果进行判断。
以下示例代码展示了如何通过 npm 或 CDN 实例化 SDK:
npm
import { AMSEasyPay } from '@alipay/ams-checkout' //包管理

const checkoutApp = new AMSEasyPay({
  environment: "sandbox",
  locale: "en_US",
  onEventCallback: ({code, message})=>{},
});
CDN
const checkoutApp = new window.AMSEasyPay({
  environment: "sandbox",
  locale: "en_US",
  onEventCallback: ({code, message})=>{},
});
  1. 使用实例对象中的 createComponent 创建一个支付组件,涉及的参数如下:
参数名称
是否必填
说明
sessionData
是
使用 sessionData 参数创建配置对象:将 createPaymentSession(快捷支付) 接口响应中的 paymentSessionData 完整数据作为此参数的值。
isNativeAppWebview
否
表示是否为 App 的网页视图。有效值为:
  • true:表示商户在 App 网页视图中集成网页版 SDK。
  • false:表示商户通过 H5 网页集成网页版 SDK。
notRedirectAfterComplete
否
Boolean 类型。有效值为:
  • false:默认值。支付完成后将自动跳转至商户页面。若该值为空,效果相同。
  • true:支付完成后不会自动跳转至商户页面。
    • 如果支付是在 SDK 内完成,则需通过客户端事件码监听后续流程。
    • 如果支付是跳转到支付方式内完成,则依赖该支付方式的回跳能力。
注意:
  • 该参数仅适用于在 SDK 内完成支付的场景。
  • 客户端返回的支付结果事件码仅用于客户端页面的跳转操作参考,交易状态的更新请以服务端 notifyPayment 或 inquiryPayment 接口返回结果为准。
创建支付组件示例:
async function create(sessionData) {
  await checkoutApp.createComponent({ 
    sessionData: sessionData, 
    isNativeAppWebview: true, // 默认为 false,表示商户通过 H5 网页集成网页版 SDK
    notRedirectAfterComplete: false // 默认设为 false,表示支付完成后将重定向至您的页面
  });
}
  1. 监听事件码处理跳转事件
通过监听 SDK_REDIRECT 事件码处理后续流程,代码示例如下:
import { AMSEasyPay } from '@alipay/ams-checkout' //包管理

const checkoutApp = new AMSEasyPay({
  environment: "sandbox",
  locale: "en_US",
  onEventCallback: ({code, message, result}) => {
    switch (code) {
      case 'SDK_REDIRECT':
        // 处理跳转逻辑
        const redirectUrls = result?.redirectUrls || {};
        const jsonString = JSON.stringify(redirectUrls);

        // 判断当前环境espJSBridge是否为空
        if (window.espJSBridge) {
            // 调用espJSBridge的sdkRedirect方法,传入重定向信息,此方法为参考,也可按照商户本身与native的通信方式进行数据传递
            window.espJSBridge.sdkRedirect(jsonString)

            // 数据传递完成后,进行组件的回收操作
            checkoutApp.unmount();
        }
        
        break;
      default:
        console.log(code);
    }
  },
});
  1. 使用 WebView 容器处理跳转事件
使用 WebView 容器处理 App 内跳转事件,以下分别为 iOS 和 Android 端示例代码:
iOS
// 步骤1:创建webview容器
let url = "https://www.merchantWeb.com"
let webView = WKWebView()
webView.load(URLRequest(url: url))
webView.configuration.userContentController.addUserScript(
    WKUserScript(
        source: "window.espJSBridge = { sdkRedirect: function(message) { window.webkit.messageHandlers.espJSBridge.postMessage(message); } }",
        injectionTime: .atDocumentStart,
        forMainFrameOnly: true
    )
)
webView.configuration.userContentController.add(self, name: "espJSBridge")

// 步骤2:处理跳转事件
extension DemoViewController: WKScriptMessageHandler {
	func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        // 监听跳转事件
        if message.name == "espJSBridge", 
            let bodyString = message.body as? String, 
            let bodyData = bodyString.data(using: .utf8), 
            let body = try? JSONSerialization.jsonObject(with: bodyData, options: []) as? [String: Any] 
        {
            let applinkUrl = body["applinkUrl"] as? String
            let schemeUrl = body["schemeUrl"] as? String
            let normalUrl = body["normalUrl"] as? String
    
            // 跳转链接
            // 尝试跳转applinkUrl
            tryRedirect(url: applinkUrl) { [weak self] success in
              // 如果失败,尝试跳转schemeUrl
              if !success {
                    self?.tryRedirect(url: schemeUrl) { [weak self] success in
                        // 如果失败,尝试跳转normalUrl
                        if !success {
                            self?.tryRedirect(url: normalUrl) { _ in }
                        }
                    }
                }
            }
        }
    }
}

func tryRedirect(url: String?, completion: @escaping (Bool) -> Void) {
    guard let url = url, let url = URL(string: url) else {
        completion(false)
        return
    }
    UIApplication.shared.open(url) { success in
        completion(success)
    }
}
Android
// 步骤1:创建webview容器
WebView webView;
webView = findViewById(R.id.webView);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JSBridgeInterface(this), "espJSBridge");
webView.loadUrl(url);

// 步骤2:处理跳转事件
public class JSBridgeInterface {

  private Context mContext;

  public JSBridgeInterface(Context context) {
    mContext = context;
  }

  // 监听跳转事件
  @JavascriptInterface
  public void sdkRedirect(String redirectInfo) {
    JSONObject jsonObject = JSONObject.parseObject(redirectInfo);
    String schemeUrl = jsonObject.getString("schemeUrl");
    String applinkUrl = jsonObject.getString("applinkUrl");
    String normalUrl = jsonObject.getString("normalUrl");

    // 尝试跳转schemeUrl
    if (openRedirectionUrl(schemeUrl, true)) {
      return;
    }

    // 尝试跳转applinkUrl
    if (openRedirectionUrl(applinkUrl, false)) {
      return;
    }

    // 尝试跳转normalUrl
    if (openRedirectionUrl(normalUrl, false)) {
      return;
    }

    showToast(mContext, "Failed to open URL");
  }

  private boolean openRedirectionUrl(String url, boolean isScheme) {
    if (TextUtils.isEmpty(url)) {
      return false;
    }
    try {
      Intent intent = isScheme
        ? Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
        : new Intent(Intent.ACTION_VIEW, Uri.parse(url));
      startActivity(mContext, intent, null);
      return true;
    } catch (Exception exception) {
      showToast(mContext, exception.getMessage()); // 显示提示信息
      return false;
    }
  }

  private void showToast(Context context, String message) {
    Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
  }
}

2. 处理 SDK 回调事件码

下列是 onEventCallback返回的事件码:
事件代码
说明
解决方案
SDK_REDIRECT
在需要跳转到第三方支付页面(如支付宝、钱包应用程序等)进行支付时触发,该事件返回的 eventResult.redirectUrls 对象包含不同环境的跳转 URL。
获取 redirectUrls 中的跳转 URL:
  • iOS 系统:优先使用 applinkUrl,其次是 schemeUrl,最后是 normalUrl。
  • Android 系统:优先使用schemeUrl,其次是 applinkUrl,最后是 normalUrl。
SDK_LAUNCH_PAYMENT_APP_ERROR
此事件码代表以下事件信息中的一种情况:
  • 跳转商户页面失败。
  • 跳转至支付方式页面失败。
Web/WAP 场景下通常不会出现跳转异常,如果出现异常建议您校验传入的重定向链接 (paymentRedirectUrl)。
SDK_CALL_URL_SUCCESS
成功打开支付方式页面或者商户页面
无需进一步操作。
SDK_PAYMENT_CANCEL
买家在未提交订单的情况下退出了支付页面。
在有效期内,可以使用 paymentSessionData 重新调用 SDK;如果已过期,需要重新请求 paymentSessionData。
SDK_PAYMENT_SUCCESSFUL
表示支付方式侧支付成功。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
注意:仅当支付流程在 SDK 内完成时返回此事件码。
SDK_PAYMENT_FAIL
表示支付方式侧支付失败。
请从 Antom 服务端获取最终的支付结果,您可以调用 inquiryPayment 接口或 notifyPayment 接口来确认支付结果。
注意:仅当支付流程在 SDK 内完成时返回此事件码。
以下示例代码展示了如何处理回调函数 onEventCallback:
function onEventCallback({ code, result }) {
  switch (code) {
    case "SDK_REDIRECT":
      // 处理跳转逻辑
      break;
    case "SDK_PAYMENT_CANCEL":
      // 在有效期内,可以使用 paymentSessionData 重新调用SDK
      break;
    default:
      break;
  }
}

3. 销毁组件

在以下情况下,调用 unmount 方法来释放 SDK 组件资源:
// 完全回收组件资源
checkoutApp.unmount();
常见问题
问:是否一定要处理 SDK 回调事件?
答:需要根据 isNativeAppWebview 的值来判断:
  • 当 isNativeAppWebview 为 true 时需要对关键回调进行处理。一般分为两类:
    • 关键回调(如支付跳转 SDK_REDIRECT):必须处理,否则支付流程会中断。
    • 其他回调:可选处理,您可以使用回调事件做一些日志埋点。
  • 当 isNativeAppWebview 为 false 时可选处理,您可以使用回调事件做一些日志埋点。

问:一个 AMSEasypay 的实例是否可以多次执行 createComponent?
答:不支持,若您要再次发起支付,请创建新的 AMSEasypay 实例。
​
问:首次支付和后续支付都需要调用 SDK 吗?
答:是的。

步骤 4:获取授权和支付结果

首次支付将返回授权及支付结果,后续支付仅返回支付结果。以下是常见场景:
类型
常见场景
获取到的异步通知情况
处理建议
首次支付
买家成功授权和支付。
授权成功,支付成功。
  • 若收到支付异步通知,则推进到对应状态。
  • 若收到授权成功异步通知,则在商户侧维护好买家和令牌的映射关系。
  • 若在商户订单关闭时未收到任何通知:
    • 您可以先将订单关闭。
    • 若后续收到支付成功通知,您可以调用 cancel 接口。
    • 若后续收到授权成功通知,您可以继续在商户侧维护好买家和令牌的映射关系。
买家授权成功,但是因为余额不足等原因支付失败。
授权成功,支付失败。
买家未授权或授权失败,但是支付成功。
授权无异步通知,支付成功。
买家未授权或授权失败,同时因为余额不足等原因支付失败。
授权无异步通知,支付失败。
买家未授权或授权失败,同时也未提交支付。
授权和支付均无异步通知。
后续支付
买家成功支付。
支付成功。
  • 若收到支付异步通知,则推进到对应状态。
  • 若在商户订单关闭时未收到任何通知:
    • 您可以先将订单关闭。
    • 若后续收到支付成功通知,您可以调用 cancel 接口。
    • 若后续收到授权成功通知,您可以继续在商户侧维护好买家和令牌的映射关系。
买家因余额不足等原因支付失败。
支付失败。
获取授权结果

获取授权结果

首次支付签约成功后,Antom 会通过 notifyAuthorization 接口给您发送授权异步通知。
  1. 配置接收异步授权通知的 webhook URL:按照 Antom Dashboard > 开发者 > 通知地址路径,为 alipay.ams.authorizations.notify 接口增加通知地址。具体操作请参阅通知地址。
以下是异步授权通知请求的代码示例:
{
  "accessToken": "28288803001319861727421828000Cv96OFlYoi17100****",
  "accessTokenExpiryTime": 2145916817000,
  "authState": "36a38e87-0453-495e-ad17-b46553b918da",
  "authorizationNotifyType": "TOKEN_CREATED",
  "userLoginId": "852-91****67",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
根据授权结果通知请求中 result.resultStatus 的值(仅返回 S)进行处理。
S:表示授权成功,并返回以下字段:
字段名
说明
accessToken
由 Antom 生成的支付令牌,用于后续支付。
accessTokenExpiryTime
支付令牌有效期。
请以下面支付令牌有效期表中的时间为准,当过了有效期后,您需要重新引导买家签约授权。
authState
发起授权而分配的专属ID。该字段的值用于验证是否与 createPaymentSession(快捷支付) 请求中指定的 authState 值一致。
authorizationNotifyType
仅返回 TOKEN_CREATED,表示买家在商户客户端成功发起快捷支付的授权。
userLoginId
买家在注册支付方式中时使用的登录 ID,该字段已经脱敏。您可以存储该账号用于后续支付时展示。
注意:并不是所有支付方式都会返回该信息。
下表为各支付方式的令牌有效期:
支付方式
支付令牌有效期
92 年
100 年
100 年
100 年
100 年
TrueMoney
100 年
无 token 返回,商户侧不需要维护 accessToken
  1. Antom 发送的通知结果由 Antom 加签,故建议您验证签名以确认通知由 Antom 发送。参考以下代码示例对授权通知进行验签:
@PostMapping("/receiveAuthNotify")
@ResponseBody
public Result receiveAuthNotify(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");

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

    // 反序列化通知体
    AlipayAuthNotify authNotify = JSON.parseObject(notifyBody, AlipayAuthNotify.class);

    if (
      authNotify != null &&
      "SUCCESS".equals(authNotify.getResult().getResultCode()) &&
      "TOKEN_CREATED".equals(authNotify.getAuthorizationNotifyType())
    ) {
      // 保存买家 PaymentMethodType 与 accessToken 的对应关系
      PaymentVO payment = authStatePayment.get(authNotify.getAuthState());
      User user = users.get(payment.getUserId());
      user
        .getPaymentMethodTypeAccessToken()
        .put(payment.getPaymentMethodType(), authNotify.getAccessToken());
      return Result.builder()
        .resultCode("SUCCESS")
        .resultMessage("success.")
        .resultStatus(ResultStatusType.S)
        .build();
    }
    // 其他类型的通知
  } catch (Exception e) {
    return Result.builder()
      .resultCode("FAIL")
      .resultMessage("fail.")
      .resultStatus(ResultStatusType.F)
      .build();
  }

  return Result.builder()
    .resultCode("SYSTEM_ERROR")
    .resultMessage("system error.")
    .resultStatus(ResultStatusType.F)
    .build();
}
  1. 收到通知后,您无需对响应通知结果做加签处理,但是对于每个通知请求均需按以下固定格式响应,与授权成功与否无关。
{
  "result": {
    "resultCode": "SUCCESS",
    "resultStatus": "S",
    "resultMessage": "success"
  }
}
获取支付结果

获取支付结果

首次支付或后续支付完成后,可通过以下方式获取支付结果:
  • 接收异步通知:接收 Antom 服务端发送的支付结果
  • 查询支付结果:调用 inquiryPayment 接口获取支付状态
接收异步通知
  1. 设置接收通知的 Webhook URL:
完成支付或支付失败时,Antom 会向您设置的 webhook URL 发送异步通知,您可以选择以下两种方法中的一种来设置接收通知的 webhook URL:
  • 订单级通知配置:通过 createPaymentSession(快捷支付)接口请求中的 paymentNotifyUrl 字段为每笔订单指定独立的通知 URL。
  • 商户级通知配置:登陆 Antom Dashboard > 开发者 > 通知地址,为 alipay.ams.payments.payNotify 接口增加通知地址。具体操作请参阅通知地址。
注意:如果以上两种方式您都配置了通知地址,则优先以接口设置为准。
以下是支付结果异步通知请求的代码示例:
{
  "actualPaymentAmount": {
    "currency": "HKD",
    "value": "98080"
  },
  "customsDeclarationAmount": {},
  "notifyType": "PAYMENT_RESULT",
  "paymentAmount": {
    "currency": "HKD",
    "value": "98080"
  },
  "paymentCreateTime": "2024-09-27T00:23:36-07:00",
  "paymentId": "202409271940108001001881E0211235544",
  "paymentRequestId": "bc93d19e-e1f6-4b68-b6b1-3d6ddc2a792a",
  "paymentTime": "2024-09-27T00:23:46-07:00",
  "pspCustomerInfo": {
    "pspCustomerId": "20881221121****",
    "pspName": "ALIPAY_HK"
  },
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
下表展示了支付结果的异步通知中 result.resultStatus 字段可能返回的值,请您根据指引进行处理。
result.resultStatus
信息
后续操作
S
支付成功。
可从中获取以下字段信息:
  • paymentrequestld:用于查询、取消和对账的支付请求 ID。
  • paymentld:表示由 Antom 生成的支付订单 ID,用于退款和对账。
  • paymentAmount:表示支付币种和金额。
F
支付失败。
建议引导买家重新下单。
  1. Antom 发送的通知结果由 Antom 加签,故建议您验证签名以确认通知由 Antom 发送。参考以下代码示例对支付通知进行验签:
/**
 * 接收通知
 *
 * @param request    请求
 * @param notifyBody 通知体
 * @return Result
 */
@PostMapping("/receiveNotify")
@ResponseBody
public Result receiveNotify(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");

  try {
    // 验证通知签名
    boolean verifyResult = WebhookTool.checkSignature(
      requestUri,
      requestMethod,
      clientId,
      requestTime,
      signature,
      notifyBody,
      ANTOM_PUBLIC_KEY
    );
    if (!verifyResult) {
      throw new RuntimeException("Invalid notify signature");
    }
    // 反序列化通知体
    JSONObject jsonObject = JSON.parseObject(notifyBody);
    String notifyType = (String) jsonObject.get("notifyType");
    if ("PAYMENT_RESULT".equals(notifyType)) {
      AlipayPayResultNotify paymentNotify = jsonObject.toJavaObject(AlipayPayResultNotify.class);
      if (paymentNotify != null && "SUCCESS".equals(paymentNotify.getResult().getResultCode())) {
        // 处理您的业务逻辑
        // 例如:将支付信息与买家关系存入数据库
        System.out.println("receive payment notify: " + JSON.toJSONString(paymentNotify));
        return Result.builder()
          .resultCode("SUCCESS")
          .resultMessage("success.")
          .resultStatus(ResultStatusType.S)
          .build();
      }
    }
    // 其他类型的通知
  } catch (Exception e) {
    // 处理错误情况
    return Result.builder()
      .resultCode("FAIL")
      .resultMessage("fail.")
      .resultStatus(ResultStatusType.F)
      .build();
  }
  return Result.builder()
    .resultCode("SYSTEM_ERROR")
    .resultMessage("system error.")
    .resultStatus(ResultStatusType.F)
    .build();
}
  1. 收到通知后,您无需对响应通知结果做加签处理,但是对于每个通知请求均需按以下固定格式响应,与订单支付成功与否无关。
{
  "result": {
    "resultCode": "SUCCESS",
    "resultStatus": "S",
    "resultMessage": "success"
  }
}
查询支付结果
Antom 提供 inquiryPayment 接口,支持主动获取订单支付状态,您可以在支付后发起轮询或发起定时任务查询。
以下代码展示了如何调用 inquiryPayment 接口:
public static void inquiryPayment() {
  AlipayPayQueryRequest alipayPayQueryRequest = new AlipayPayQueryRequest();

  // 替换为您的 paymentRequestId
  alipayPayQueryRequest.setPaymentRequestId("yourPaymentRequestId");

  AlipayPayQueryResponse alipayPayQueryResponse = null;
  try {
    alipayPayQueryResponse = CLIENT.execute(alipayPayQueryRequest);
  } catch (AlipayApiException e) {
    String errorMsg = e.getMessage();
    // 处理错误情况
  }
}
以下是请求报文的示例:
{
  "paymentRequestId": "bc93d19e-e1f6-4b68-b6b1-3d6ddc2a****"
}
以下是响应报文的示例:
{
  "actualPaymentAmount": {
    "currency": "USD",
    "value": "1"
  },
  "customsDeclarationAmount": {
    "currency": "CNY",
    "value": "7"
  },
  "paymentAmount": {
    "currency": "USD",
    "value": "1"
  },
  "paymentId": "20250305194010800100188690281017336",
  "paymentMethodType": "ALIPAY_CN",
  "paymentRedirectUrl": "https://checkout.antom.com/checkout-page/pages/payment/index.html?sessionData=%2BCUim8L0KviXagaygm9xBL5jZ%2F75w6gAX1nn8pcuFuGkIsMoHtD6U88YSyMrMJvorbwnBg5uQv8e6pyvIpjDQQ%3D%3D%26%26SG%26%26188%26%26eyJleHRlbmRJbmZvIjoie1wiT1BFTl9NVUxUSV9QQVlNRU5UX0FCSUxJVFlcIjpcInRydWVcIixcImxvY2FsZVwiOlwiZW5fVVNcIixcImRpc3BsYXlBbnRvbUxvZ29cIjpcInRydWVcIn0iLCJwYXltZW50U2Vzc2lvbkNvbmZpZyI6eyJwYXltZW50TWV0aG9kQ2F0ZWdvcnlUeXBlIjoiQUxMIiwicHJvZHVjdFNjZW5lIjoiQ0hFQ0tPVVRfUEFZTUVOVCIsInByb2R1Y3RTY2VuZVZlcnNpb24iOiIxLjAifSwic2VjdXJpdHlDb25maWciOnsiYXBwSWQiOiIiLCJhcHBOYW1lIjoiT25lQWNjb3VudCIsImJpelRva2VuIjoiNlRjZGJyMnJGM3JQWXg0aGtWckhxYnZqIiwiZ2F0ZXdheSI6Imh0dHBzOi8vaW1ncy1zZWEuYWxpcGF5LmNvbS9tZ3cuaHRtIiwiaDVnYXRld2F5IjoiaHR0cHM6Ly9vcGVuLXNlYS1nbG9iYWwuYWxpcGF5LmNvbS9hcGkvb3Blbi9yaXNrX2NsaWVudCIsIndvcmtTcGFjZUlkIjoiIn0sInNraXBSZW5kZXJQYXltZW50TWV0aG9kIjpmYWxzZX0%3D",
  "paymentRequestId": "PAYMENT_20250305220039086_AUTO",
  "paymentResultCode": "SUCCESS",
  "paymentResultMessage": "success.",
  "paymentStatus": "SUCCESS",
  "paymentTime": "2025-03-05T06:02:34-08:00",
  "pspCustomerInfo": {
    "pspName": "ALIPAY_CN"
  },
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
请根据响应中 paymentStatus 参数的值进行处理,具体返回值请参阅 inquiryPayment 接口文档。
常见问题
问:支付通知何时发送?
答:这取决于支付是否完成:如果支付成功完成,Antom 通常会在 3 到 5 秒内发送异步通知。
​
问:授权失败是否有异步通知发送?
答:只有授权成功才会有异步通知发送,授权失败不会返回异步通知。
​
问:异步通知会被重新发送吗?
答:是的,对于以下情况,异步通知将在 24 小时内自动重新发送:
  • 如果由于网络原因没有收到异步通知;
  • 如果您收到 Antom 的异步通知,但您没有按照返回收到确认信息的示例代码格式进行响应。
通知可以重发最多 8 次,或者直到收到正确的响应以终止传递。发送间隔如下:0 分钟,2 分钟,10 分钟,10 分钟,1 小时,2 小时,6 小时,15 小时。
​
问:授权通知和支付结果通知是分开的,授权通知一定是比支付结果通先收到吗?
答:由于网络稳定性不可控,可能会出现授权通知比支付结果通知晚到的情况。
​
问:是否支持授权查询接口?
答:目前暂未支持该能力。
​
问:在响应异步通知时,我需要添加数字签名吗?
答:如果您收到 Antom 的异步通知,您需要按照返回收到确认信息的示例代码格式返回响应,但不需要对响应进行签名。

支付后集成

取消授权

授权完成后,买家可通过商户侧或支付方式侧取消授权。取消后,原授权令牌立即失效且不可再用于支付。详情请参阅取消授权。

取消交易

订单支持在时效窗口内(截至 D+1 日 00:15 GMT+8)通过 cancel 接口终止交易。详情请参阅cancel。

退款

Antom 退款规则及操作流程,请参阅退款。

账单

交易完成后,请使用 Antom 财务报告进行对账。结算规则及对账操作请参阅对账。

最佳实践

Antom 为您提供以下最佳实践方案,请参阅最佳实践了解详情:
  • 智能风控服务
  • 安全扩展包
  • 结果页跳转后的订单查询
  • 商户侧主动取消交易
  • 支付失败重试