Antom, leading provider of tailored payment solutionsAntom, leading provider of tailored payment solutions

快捷支付(Android)

注意:本文档已停止维护并下线归档,请访问 https://docs.antom.com/ 获取最新内容。

本文为您介绍支持从桌面浏览器或移动浏览器接收付款的集成解决方案。集成后,您可以接入数字钱包、银行卡和银行转账等多种支付方式。

用户体验

以下图片分别展示了钱包和银行转账的用户体验。在首次支付时,买家可以启用免密支付,这样后续支付无需再输入支付密码。后续支付的全流程都将在您的网站或应用上进行。

钱包

当使用钱包付款时,整个支付流程会保留在您的网站或应用程序内,买家可以在首次付款时启用免密支付,后续付款无需再输入支付密码。

第一次使用钱包支付

在首次支付时,买家完成支付授权流程以确保后续免密支付。

1.png

银行转账

对于银行转账支付方式,以下图表展示了首次支付和后续支付的用户体验。

首次使用银行转账支付

在首次支付时,用户需要输入支付密码和验证码。

网络异常,图片无法展示
|

支付流程

首次支付

当买家选择 Antom 提供的支付方式时您需要采集支付请求 ID、订单金额、支付方式、订单详情、支付重定向链接和支付结果通知链接等必要信息。然后调用 支付会话创建(快捷支付)接口来下单,并调用 Antom SDK 中的相关方法完成支付流程。

流程图1中文.png

  1. 买家进入结账页面
  2. 创建支付会话请求
    在买家选择支付方式并提交订单后,您可以通过调用 支付会话创建(快捷支付)接口来获取支付会话。
  3. 调用客户端 SDK
    在客户端通过支付会话调用 SDK。SDK 会基于支付方式的特点收集支付要素、展示代码信息、进行重定向、调用,并引导买家完成支付。
  4. 获取授权结果
  1. 获取支付结果
    通过以下两种方法获取支付结果:

集成步骤

要获取买家授权并进行快捷支付,您需要完成以下集成步骤:

  1. 创建支付会话
  2. 调用SDK
  3. 获取授权或支付结果

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

当买家选择由 Antom 提供的支付方式时,您需要收集支付请求 ID、订单金额、支付方式、订单描述、支付重定向页面链接和支付结果通知链接等必要信息。随后,通过以下步骤调用 支付会话创建(快捷支付)接口来创建支付会话:

  1. 安装接口库
  2. 初始化请求实例
  3. 调用 支付会话创建(快捷支付)接口

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

安装接口库

您可以在 GitHub 上找到最新版本。

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

初始化请求实例

创建一个单例资源以向 Antom 发起请求。

copy
import com.alipay.global.api.AlipayClient;
import com.alipay.global.api.DefaultAlipayClient;
import com.alipay.global.api.model.constants.EndPointConstants;

public class Sample {
    public static final String        CLIENT_ID            = "";
    public static final String        ANTOM_PUBLIC_KEY     = "";
    public static final String        MERCHANT_PRIVATE_KEY = "";

    private final static AlipayClient CLIENT               = new DefaultAlipayClient(
            EndPointConstants.SG, MERCHANT_PRIVATE_KEY, ANTOM_PUBLIC_KEY, CLIENT_ID);

}

创建支付会话

首次支付

调用 支付会话创建(快捷支付)接口指定以下参数来创建支付会话:

参数

是否必填

描述

paymentRedirectUrl

支付完成后,买家将被重定向至的商家页面链接。

authState

您用于发起授权而分配的专属ID,在后续免密支付中获取令牌。该参数仅在首次支付时传递,后续支付无需传递此参数。

paymentNotifyUrl

支付结果通知地址。这个地址必须是 HTTPS。

paymentSessionExpiryTime

支付会话超时。默认为 1 小时,可以设置在 1 小时内。该值的格式遵循 ISO 8601 标准。例如,2019-11-27T12:01:01+08:00。

userLoginId

买家支付方式的登录账户可能是买家的邮箱地址或者手机号码。

order.buyer: referenceBuyerId/buyerPhoneNo/buyerEmail

传递买家信息以供风险决策。传递 referenceBuyerIdbuyerPhoneNobuyerEmail 其中一个参数即可。

以上参数并非完整参数,请参阅 支付会话创建(快捷支付)接口以获取完整参数列表以及特定支付方式的额外要求。

以下示例代码展示如何调用 支付会话创建(快捷支付)接口:

copy
public static void createPaymentSession() {
    AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
    alipayPaymentSessionRequest.setProductCode(ProductCodeType.AGREEMENT_PAYMENT);
    alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.EASY_PAY);

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

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

    //set settlement currency
    SettlementStrategy settlementStrategy = new SettlementStrategy();
    settlementStrategy.setSettlementCurrency("USD");
    alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

    // set paymentMethod
    PaymentMethod paymentMethod = PaymentMethod.builder().paymentMethodType("ALIPAY_HK").build();
    alipayPaymentSessionRequest.setPaymentMethod(paymentMethod);

    // set agreementInfo
    // replace with your authState
    String authState = UUID.randomUUID().toString();
    // The login ID that the user used to register in the payment method client. The login ID can be the user's email address or phone number.
    // Specify this parameter to free users from manually entering their login IDs.
    String userLoginId = "852-91234567";
    AgreementInfo agreementInfo = AgreementInfo.builder().authState(authState).userLoginId(userLoginId).build();
    alipayPaymentSessionRequest.setAgreementInfo(agreementInfo);

    // 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();
    alipayPaymentSessionRequest.setOrder(order);

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

    // replace with your redirect url
    alipayPaymentSessionRequest.setPaymentRedirectUrl("http://www.yourRedirectUrl.com");

    AlipayPaymentSessionResponse alipayPaymentSessionResponse;
    try {
        alipayPaymentSessionResponse = CLIENT.execute(alipayPaymentSessionRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // handle error condition
    }
}

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

copy
{
    "agreementInfo": {
        "authState": "2b7a4a24-b909-4633-8ffe-8ed3648e99b4",
        "userLoginId": "852-91234567"
    },
    "order": {
        "buyer": {
            "referenceBuyerId": "yourBuyerId"
        },
        "orderAmount": {
            "currency": "HKD",
            "value": "98080"
        },
        "orderDescription": "antom api testing order",
        "referenceOrderId": "1a57b8cd-9a25-4d44-94f7-3467a4ebe532"
    },
    "paymentAmount": {
        "currency": "HKD",
        "value": "98080"
    },
    "paymentMethod": {
        "paymentMethodType": "ALIPAY_HK"
    },
    "paymentNotifyUrl": "http://www.yourNotifyUrl.com",
    "paymentRedirectUrl": "http://www.yourRedirectUrl.com",
    "paymentRequestId": "bc93d19e-e1f6-4b68-b6b1-3d6ddc2a792a",
    "productCode": "AGREEMENT_PAYMENT",
    "productScene": "EASY_PAY",
    "settlementStrategy": {
        "settlementCurrency": "USD"
    }
}

以下代码是包含了以下参数的响应示例:

  • paymentSessionData:需要转发给您客户端的支付会话数据。
  • paymentSessionExpiryTime:支付会话的过期时间。
copy
{
    "paymentSessionData": "ZqeGpu7pbMb/I3dNWTTEL3o4w5mXh20j13VnmsE1p3cjK3CVpnMXY7BfQlIvwNqQWtXHEMUo0R5pQwnSyNtxTA==&&SG&&188&&eyJh***",
    "paymentSessionExpiryTime": "2024-09-27T15:57:30+08:00",
    "paymentSessionId": "ZqeGpu7pbMb/I3dNWTTEL3o4w5mXh20j13VnmsE1p3fI21eGbgq240lFVquZsLrM",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

常见问题

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

答:为避免某些支付方式的不兼容,不要在请求的字段中使用中文字符。

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

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

步骤 2:调用 SDK 客户端

安装

版本要求:Android 4.4(API level 19)或更高版本。

注意:暂不支持 ‌Flutter 和 React Native(RN)开发框架。

请查阅 Android 端集成 SDK 资源包文档来集成 SDK 资源包。

实例化 SDK

创建 SDK 实例并指定基本配置。创建配置对象包括以下方法:

参数

是否必填

描述

setLocale

用于指定语言信息。根据支付方式所在地区选择合适的值。此参数的有效值如下所示,如果使用了以下列表中未列出的值,将使用本地语言代替。

  • en_US:英语
  • in_ID印尼语
  • ms_MY马来语
  • tl_PH菲律宾语
  • ko_KR韩语
  • zh_CN简体中文
  • zh_HK繁体中文

setOption

用于指定是否使用默认加载模式和沙箱环境。有效值包括:

  • "sandbox","true":沙箱环境
  • "sandbox","false":生产环境
  • "showLoading","true":使用默认加载模式。
  • "showLoading","false":不使用默认加载模式。
  • "windowGravity","LANDSCAPE_CENTER":横屏模式下窗口居中。
  • "windowGravity","LANDSCAPE_RIGHT":横屏模式下窗口靠右对齐。

以下示例代码展示了如何初始化 SDK:

copy
// Step 1: Create the AMSEasyPayConfiguration type.
AMSEasyPayConfiguration configuration = new AMSEasyPayConfiguration();
configuration.setLocale(new Locale("en", "US"));
// Specify showLoading as true (default value) to use the default loading pattern. Specify it as false to customize the loading animation based on onEventCallback.
configuration.setOption("showLoading", "true");
// Set the sandbox environment. If you leave it empty, the production environment is used by default.
configuration.setOption("sandbox", "true");
// Set the callback to monitor payment events on the checkout page.
configuration.setOnCheckoutListener(new OnCheckoutListener() {
    @Override
    public void onEventCallback(String eventCode, AMSEventResult eventResult) {
        if("SDK_PAYMENT_CANCEL".equals(eventCode)){
            // Add interactive prompts.
        }
        Toast.makeText(activity, "eventCode=" + eventCode + " message=" + message, Toast.LENGTH_SHORT).show();
    }
});

// Instantiate AMSEasyPay. Create a new AMSEasyPay component before each createPaymentSession API request.
AMSEasyPay checkout = new AMSEasyPay.Builder(activity, configuration).build();

调用 SDK

调用 createComponent 方法:

参数名称

是否必需

描述

sessionData

使用 sessionData 参数创建配置对象:将 支付会话创建(快捷支付)接口响应中的 paymentSessionData 完整数据作为此参数的值。

在以下情况调用 onDestroy 以回收 SDK 组件资源:

以下示例代码展示了如何调用 SDK:

copy
checkout.createComponent(activity, sessionData);

// 回收组件资源
checkout.onDestroy();

步骤 3:获取授权或支付结果 服务端

对于首次支付,您可以获取授权和支付结果,而对于后续支付,仅提供支付结果。

您可以通过以下方法之一获取授权或支付结果:

  • 接收异步通知
  • 查询结果

接收异步通知

获取授权结果

授权成功后, Antom 会通过 授权结果通知 接口给您发送异步通知。当您收到通知,您需要按照返回收到确认信息返回响应。同时,您必须在系统中更新买家的授权状态,并在您的授权管理页面上显示从通知中获取的买家无敏感信息账户。

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

copy
{
    "accessToken": "28288803001319861727421828000Cv96OFlYoi17100****",
    "accessTokenExpiryTime": 2145916817000,
    "authState": "36a38e87-0453-495e-ad17-b46553b918da",
    "authorizationNotifyType": "TOKEN_CREATED",
    "userLoginId": "852-91****67",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

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

常见问题

问:支付结果通知何时发送?

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

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

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

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

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

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

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

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

问:在支付结果通知中,我需要使用哪些关键参数?

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

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

:授权结果通知中我需要使用哪些关键参数?

请注意以下关键参数:

  • result表示订单的支付结果。
  • authState由商户生成的资产绑定请求 ID。
  • accessToken Antom 生成的代扣 ID,用于后续支付。
  • userLoginId用户的无敏感信息账户 ID。

支付结果查询结果

您可以调用 支付结果查询 接口来发起对订单结果的查询。

参数

是否必需

说明

paymentRequestId

由您生成的支付请求 ID。

以上参数并非完整参数,请参考 支付结果查询 接口获取完整参数以及特定支付方式的额外要求。

以下示例代码展示了如何调用 支付结果查询 接口:

copy
public static void inquiryPayment() {
    AlipayPayQueryRequest alipayPayQueryRequest = new AlipayPayQueryRequest();

    // replace with your paymentRequestId
    alipayPayQueryRequest.setPaymentRequestId("yourPaymentRequestId");

    AlipayPayQueryResponse alipayPayQueryResponse = null;
    try {
        alipayPayQueryResponse = CLIENT.execute(alipayPayQueryRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // handle error condition
    }
}

以下是请求报文的示例:

copy
{
  "paymentRequestId": "bc93d19e-e1f6-4b68-b6b1-3d6ddc2a792a"
}

以下是响应报文的示例:

copy
{
    "cardInfo": {},
    "paymentResultCode": "SUCCESS",
    "paymentRequestId": "bc93d19e-e1f6-4b68-b6b1-3d6ddc2a792a",
    "paymentResultInfo": {},
    "paymentAmount": {
        "currency": "HKD",
        "value": "98080"
    },
    "result": {
        "resultStatus": "S",
        "resultCode": "SUCCESS",
        "resultMessage": "success."
    },
    "actualPaymentAmount": {
        "currency": "HKD",
        "value": "98080"
    },
    "paymentId": "202409271940108001001881E0211235544",
    "paymentResultMessage": "success",
    "pspCustomerInfo": {
        "pspName": "ALIPAY_HK"
    },
    "paymentTime": "2024-09-27T00:23:46-07:00",
    "customsDeclarationAmount": {},
    "paymentStatus": "SUCCESS"
}

常见问题

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

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

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

答:请注意这些关键参数:

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

示例代码

使用客户端 SDK 的完整示例代码:

copy
// 步骤一:创建AMSEasyPayConfiguration类型
AMSEasyPayConfiguration configuration = new AMSEasyPayConfiguration();
configuration.setLocale(new Locale("en", "US"));
// 设置showLoading参数为true(默认值)时使用内部loading页面,false时商户可通过onEventCallback中的事件自定义loading动画
configuration.setOption("showLoading", "true");
// 设置沙箱环境,不设置为线上正式环境
configuration.setOption("sandbox", "true");
// 在横向模式下将窗口居中,如果未设置则向右对齐。
configuration.setOption("windowGravity", "LANDSCAPE_CENTER");
// 设置收银台回调监听
configuration.setOnCheckoutListener(new OnCheckoutListener() {
    @Override
    public void onEventCallback(String eventCode, AMSEventResult eventResult) {
        if("SDK_PAYMENT_CANCEL".equals(eventCode)){
            // 添加互动提示
        }
        Toast.makeText(activity, "eventCode=" + eventCode + " message=" + message, Toast.LENGTH_SHORT).show();
    }
});
// 创建AMSEasyPay实例化,每次创建支付会话时,需要重新创建一个AMSEasyPay组件
AMSEasyPay checkout = new AMSEasyPay.Builder(activity, configuration).build();

// 步骤二:创建支付会话
String sessionData = createSessionData()

// 步骤三:初始化并将sessionData传递给前端组件
checkout.createComponent(activity,sessionData);

// 步骤四:当买家退出支付页面时调用onDestroy回收组件
checkout.onDestroy(activity,sessionData);

事件码

您可能会看到两种类型的事件码:

  • 状态码:在组件运行生命周期内,通过 onEventCallback 回调函数返回。
  • 错误码:在组件初始化阶段,通过 onEventCallback 或 onError 回调函数返回。

类型

代码

描述

后续操作

状态码

SDK_START_OF_LOADING

配置 showLoading 为 false 时,使用自定义动画,您可在这个时机渲染并展示自定义加载动画。

无需进一步操作。

SDK_END_OF_LOADING

配置 showLoading 为 false 时,使用自定义动画,您可在这个时机结束自定义加载动画。

无需进一步操作。

SDK_CALL_URL_ERROR

此事件码代表以下事件信息中的一种情况:

Web/WAP 场景下通常不会出现跳转异常,如果出现异常建议您校验重定向链接。在 App 场景下,如果频繁出现跳转异常,请联系 Antom 技术支持,排查跳转或唤端问题。

无需进一步操作。

SDK_PAYMENT_CANCEL

表示用户取消支付,即用户未提交订单退出支付页面。您可使用有效期内的 paymentSessionData 重新调用 SDK。如已过期,需要重新发起 支付会话创建(快捷支付)请求。

无需进一步操作。

错误码

SDK_INTERNAL_ERROR

SDK 内部错误。

请联系 Antom技术支持。

SDK_CREATEPAYMENT_PARAMETER_ERROR

createComponent

函数传入参数异常。

请检查参数是否正确并重新初始化组件。

SDK_INIT_PARAMETER_ERROR

AMSEasyPay

函数传入参数异常。

请检查参数是否正确并重新实例化SDK。

SDK_CREATECOMPONENT_ERROR

组件初始化异常。

请联系 Antom 技术支持。

SDK_SUBMIT_NETWORK_ERROR

因网络原因,接口调用失败。在 submit 函数提交中可能会出现。

请尝试再次提交。