托管卡订阅支付

订阅支付是一种周期性自动扣款解决方案,可以帮助您完成周期性自动收款。仅需一次授权即可绑定支付账户享受持续的订阅服务,且支持动态调整订阅配置(如修改周期/金额、取消续订或终止服务等)。整个流程既便捷高效,又安全可靠。

本文为您介绍如何通过托管模式集成银行卡支付方式。通过托管模式集成银行卡支付您可以将支付信息采集环节交由 Antom 处理。由于敏感的持卡人数据不经过您的服务器,此模式可帮助您免除 PCI-DSS 的合规负担,无需您自行构建符合 PCI-DSS 标准的支付环境,您也无需获取 PCI 合规资质。

订阅支付体验

以下为首次授权和后续扣款的用户体验:

注意:订阅支付需要绑定卡资产时,首笔交易需要买家发起并通过 3DS 验证流程以完成身份核验。

PC 端:

首次支付用户体验图.png

Mobile 端:

首次支付用户体验-mobile.png

订阅生命周期

以下图片分别展示了整个订阅周期、订阅创建以及周期扣款的流程:

下图展示了整个订阅生命周期,包括创建订阅、签约绑定支付方式、完成首次扣款,以及在必要时发起退款等环节,旨在保障订阅的正常生效与费用处理的安全透明

1.png 2.png

集成准备

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

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

集成步骤

开始集成,请按照以下步骤操作:

  1. 添加支付方式列表
  2. 创建订阅支付请求
  3. 跳转至 Antom 卡信息收集页面(normalUrl
  4. 授权结果通知
  5. 请款并获取请款通知
  6. 获取订阅通知

步骤 1:添加支付方式列表 客户端

在买家下单页面的支付方式列表中,展示本次需要集成的卡品牌标识和名称,供买家根据自身需求和偏好选择。

注意:支付方式列表页面需要由您自行实现。

步骤 2:创建订阅支付请求 服务端

可跳转到 Antom 卡信息收集页面来收集买家的支付信息(如卡号、有效期等)、订阅信息(如扣款金额,周期)、订单详情,设备及支付金额等信息,并调用 pay(单笔支付)接口来创建授权订阅支付请求:

创建授权订阅支付请求包含以下关键参数:

类型

参数

是否必传

描述

基础字段

paymentRequestId

授权支付请求单号,每次都需要唯一。

paymentAmount.currency

支付币种。

paymentAmount.value

支付金额。按币种的最小单位设置,除 JPY、KRW 为元,其他均为分。

settlementStrategy.settlementCurrency

结算币种。

paymentMethod.paymentMethodType

支付方式枚举值,卡支付场景下的值为 CARD

paymentFactor.isAuthorization

表示支付场景是否为授权场景。卡支付场景下固定传 true,指定为授权请款模式。

paymentFactor.captureMode

请款模式。默认由 Antom 替商户完成请款。如对首笔创建订阅交易需要设置为由您自行发起请款,可将该字段设置为 MANUAL,并在授权成功后调用 capture(单笔支付)接口

productCode

产品码,在此场景中,此字段固定传 CASHIER_PAYMENT

paymentRedirectUrl

商户端支付结果页。需传入 HTTPS 地址,您根据支付结果进行动态渲染。

paymentNotifyUrl

授权支付结果通知地址,需传入 HTTPS 地址。此字段可通过接口传递,也可通过 Antom Dashboard 设置为固定值。若两者都设置,则接口传递的值优先。

设备字段

env.terminalType

指定买家发起交易的端类型:

  • WEB:客户端终端类型为网站,通过 PC 浏览器打开。
  • WAP:客户端终端类型为 H5 页面,通过移动浏览器打开。
  • APP:客户端终端类型为移动应用。

env.clientIp

买家的 IP 地址。

订单字段

order.orderAmount

订单金额。

order.referenceOrderId

商户端订单号。

order.orderDescription

商户端订单描述。

order.buyer

商户端买家信息。至少需要提供以下三者其中一个信息:

  • order.buyer.referenceBuyerId
  • order.buyer.buyerPhoneNo
  • order.buyer.buyerEmail

订阅字段

subscriptionInfo.subscriptionDescription

订阅描述。

subscriptionInfo.subscriptionStartTime

订阅关系生效时间,格式类似 2019-11-27T12:01:01+08:00,+08:00 代表东八区

subscriptionInfo.subscriptionNotifyUrl

订阅关系通知接收地址,需传入 HTTPS 地址。

subscriptionInfo.trials

订阅的试用信息列表。若您提供的订阅服务包含试用或折扣优惠,可通过此参数指定。

subscriptionInfo.periodRule

订阅周期,支持 YEARMONTHWEEKDAY 四类周期。

卡支付信息参数

paymentMethod.paymentMethodMetaData.

is3DSAuthentication

您需要根据当前交易的风险和拒付情况,决定交易是否进行 3DS 认证。有效值为:

  • true:指交易发起 3DS 认证。
  • false:指交易不进行 3DS 认证。如果此参数为空或未传递,默认为此值。

注意:卡支付场景下需设置为 true 做 3D 交易,保障首笔银行卡支付对买家进行身份验证。

以上参数是创建订阅支付请求的基本参数,完整参数和特定支付方式的额外要求请参考 pay(单笔支付)

以下示例代码展示了如何调用 pay(单笔支付)接口:

copy
    public static void pay() {
        AlipayPayRequest alipayPayRequest = new AlipayPayRequest();

        // 设置订阅信息
        SubscriptionInfo subscriptionInfo = new SubscriptionInfo();
        subscriptionInfo.setSubscriptionDescription("TEST_SUBSCRIPTION");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
        subscriptionInfo.setSubscriptionStartTime(ZonedDateTime.now().format(formatter));
        subscriptionInfo.setSubscriptionEndTime(ZonedDateTime.now().plusYears(1).format(formatter));
        subscriptionInfo.setSubscriptionNotifyUrl("https://your.example.com/subscriptionNotify");

        // 设置订阅周期规则
        PeriodRule periodRule = new PeriodRule();
        periodRule.setPeriodType("YEAR");
        periodRule.setPeriodCount(1);
        subscriptionInfo.setPeriodRule(periodRule);
        alipayPayRequest.setSubscriptionInfo(subscriptionInfo);

        // 设置订单信息
        String rng = UUID.randomUUID().toString();
        Amount amount = Amount.builder().currency("SGD").value("5200").build();

        Order order = new Order();
        order.setOrderAmount(amount);
        order.setOrderDescription("TEST_ORDER_" + rng);
        order.setReferenceOrderId("ORDER_" + rng);
        Buyer buyer = new Buyer();

        // 设置订单描述
        order.setOrderDescription("Sample subscription order");

        // 替换为您的 orderId
        order.setReferenceOrderId("ORDER_" + rng);

        // 设置买家信息
        buyer.setBuyerEmail("buyer@example.com");
        buyer.setBuyerPhoneNo("123456789");
        buyer.setReferenceBuyerId("BUYER_" + rng);
        order.setBuyer(buyer);

        alipayPayRequest.setOrder(order);

        // 设置环境信息
        Env env = new Env();
        env.setTerminalType(TerminalType.WEB);
        env.setClientIp("112.80.248.78");
        alipayPayRequest.setEnv(env);

        alipayPayRequest.setPaymentAmount(amount);

        // 设置支付方式
        PaymentMethod paymentMethod = new PaymentMethod();
        paymentMethod.setPaymentMethodType("CARD");

        // 设置支付方式扩展信息
        Map<String, Object> metaData = new HashMap<>();
        metaData.put("is3DSAuthentication", "true");
        paymentMethod.setPaymentMethodMetaData(metaData);
        alipayPayRequest.setPaymentMethod(paymentMethod);

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

        // 设置授权请款模式
        PaymentFactor paymentFactor = new PaymentFactor();
        paymentFactor.setIsAuthorization(true);
        alipayPayRequest.setPaymentFactor(paymentFactor);

        // 替换为您的通知地址
        alipayPayRequest.setPaymentNotifyUrl("https://www.your-notify.example.com/payments/notify");

        // 替换为您的跳转 url
        alipayPayRequest.setPaymentRedirectUrl("https://www.your-merchant.example.com/redirect");

        // 替换为您的 paymentRequestId
        alipayPayRequest.setPaymentRequestId("PAY_XXXXXX" + rng);

        // 设置产品码
        alipayPayRequest.setProductCode(ProductCodeType.CASHIER_PAYMENT);

        // 设置请求路径
        alipayPayRequest.setPath(PAY_PATH);

        // 支付
        AlipayPayResponse alipayPayResponse = null;
        try {
            alipayPayResponse = CLIENT.execute(alipayPayRequest);
        } catch (AlipayApiException e) {
            String errorMsg = e.getMessage();
            // 处理错误信息
        }
    }

以下为请求报文示例:

copy
{
    "env": {
        "clientIp": "112.*.*.*",
        "terminalType": "WEB"
    },
    "order": {
        "buyer": {
            "buyerEmail": "buy***@example.com",
            "buyerPhoneNo": "123456789",
            "referenceBuyerId": "BUYER_eeae1497-8f54-4359-977e-3b5e858335af"
        },
        "orderAmount": {
            "currency": "SGD",
            "value": "5200"
        },
        "orderDescription": "Sample subscription order",
        "referenceOrderId": "ORDER_eeae1497-8f54-4359-977e-3b5e858335af"
    },
    "paymentAmount": {
        "currency": "SGD",
        "value": "5200"
    },
    "paymentFactor": {
        "isAuthorization": true
    },
    "paymentMethod": {
        "paymentMethodMetaData": {
            "is3DSAuthentication": "true"
        },
        "paymentMethodType": "CARD"
    },
    "paymentNotifyUrl": "https://www.your-notify.example.com/payments/notify",
    "paymentRedirectUrl": "https://www.your-merchant.example.com/redirect",
    "paymentRequestId": "PAY_XXXXXXeeae1497-8f54-4359-977e-3b5e858335af",
    "productCode": "CASHIER_PAYMENT",
    "settlementStrategy": {
        "settlementCurrency": "SGD"
    },
    "subscriptionInfo": {
        "periodRule": {
            "periodCount": 1,
            "periodType": "YEAR"
        },
        "subscriptionDescription": "TEST_SUBSCRIPTION",
        "subscriptionEndTime": "2027-03-12T09:59:38+08:00",
        "subscriptionNotifyUrl": "http://localhost:8443/payment/receiveSubscriptionNotify",
        "subscriptionStartTime": "2026-03-12T09:59:38+08:00"
    }
}

以下代码展示了一个响应的示例,其中包含以下关键参数:

  • result.resultStatus:判断授权状态。
  • normalUrl为 Antom 的卡信息收集页面。
copy
{
  "normalUrl": "https://sandbox.example.com/checkout/page?sessionData=SESSION_SAMPLE_ABC123",
  "paymentActionForm": "{\"method\":\"GET\",\"paymentActionFormType\":\"RedirectActionForm\",\"redirectUrl\":\"https://sandbox.example.com/checkout/page?sessionData=SESSION_SAMPLE_ABC123\"}",
    "paymentAmount": {
        "currency": "SGD",
        "value": "5200"
    },
    "paymentCreateTime": "2026-03-11T18:59:40-07:00",
    "paymentId": "202603121940109000001888F0284122695",
    "paymentRequestId": "PAY_XXXXXXeeae1497-8f54-4359-977e-3b5e858335af",
    "paymentResultInfo": {},
    "redirectActionForm": {
        "method": "GET",
        "redirectUrl": "https://sandbox.example.com/checkout/page?sessionData=SESSION_SAMPLE_ABC123"
    },
    "result": {
        "resultCode": "PAYMENT_IN_PROCESS",
        "resultMessage": "payment in process",
        "resultStatus": "U"
    }
}

下表展示了响应代码中 result.resultStatus 字段可能返回的值,请您根据指引进行处理:

result.resultStatus

信息

后续操作

F

授权支付失败。

关闭当前交易或重新更换 paymentRequestId 再次下单。

U

授权支付进行中。

  • 若返回了 normalUrl:商户前端会跳转到 normalUrl,您需要同时存储 paymentId 用于后续的请款和退款。
  • 若没有返回 normalUrl则表示交易异常,您可关闭当前交易或更换 paymentRequestId 重新下单。

注意如果您未收到响应报文,可能是网络超时所致。您可关闭当前交易或更换 paymentRequestId 重新下单。

常见问题

问:首笔交易是否一定要强制 3D 验证

答:首笔交易是买家参与的交易,需要进行身份验证,用于保障后续买家不在场的周期性扣费的安全。因此对于首笔交易的身份验证有如下要求:

  • 通过 Antom 完成 3D 验证:买家选择卡支付,设置 paymentMethodTypeCARD,均需要完成 3DS 交易,以完成买家身份验证。可以在 pay(单笔支付)接口请求中设置参数 is3DSAuthentication 的值为 true通过 Antom 3DS 实现。

步骤 3:跳转至 Antom 卡信息收集页面(normalUrl 客户端

商户服务端拿到 Antom 返回的卡信息收集页面地址(normalUrl )后,将该地址传递给前端,由商户前端跳转至 Antom 卡信息收集页面。

以下为商户前端加载 normalUrl 的示例代码:

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

以下图片展示了 Web 和 App 端重定向至卡信息采集页面(normalUrl)的用户体验:

1.png

常见问题

问:如何处理不同的支付体验?

答:通过 API 集成接入卡支付返回的 normalUrl 均为 H5 链接,对于 WEB 和 WAP 直接跳转即可,对于 APP 建议您通过 WebView 形式加载 normalUrl,提升用户体验。

问:paymentRedirectUrl 在传参上有些什么注意点?

答:默认设置为 HTTPS 地址,同时 URL 中的特殊字符能进行编码处理,否则会支付异常。

问:支付结果页内容如何展示?

答:您需要通过在 pay(单笔支付)接口中指定 paymentRedirectUrl 字段来提供一个 HTTPS 地址。该地址用于在商户端显示支付结果。在支付成功和支付失败的情况下,可能都有入口可以从支付方式端回跳到商户页面。因此,请勿将 paymentRedirectUrl 固定写成“订阅创建成功页面”,而是以服务端返回的结果为准,避免引起买家误解。

问:回跳商户结果页是否代表订阅创建成功?

答:不能仅凭回跳商户页面来判断订阅创建是否成功,具体有以下三种情况

  • 买家支付成功后,可能因网络等原因导致未能回跳至商户页。
  • 即使买家未完成支付,也可能通过支付方式端的入口回跳至商户页面。
  • 即使买家完成支付,也可能因为请款失败而导致订阅关系没有生效。

步骤 4:授权结果通知 服务端

无论授权成功或失败,Antom 都会使用 notifyPayment(订阅)接口将支付结果发送给您。请按以下步骤获取授权支付结果通知:

  1. 设置接收通知的 webhook URL: pay(单笔支付)接口请求的 paymentNotifyUrl 参数设置和 Antom Dashboard 配置。

以下为异步通知请求体的代码示例:

copy
{
  "notifyType": "PAYMENT_RESULT",
  "result": {
    "resultCode": "SUCCESS",
    "resultStatus": "S",
    "resultMessage": "success"
  },
  "paymentRequestId": "2020010123456789XXXX",
  "paymentId": "2020010123456789XXXX",
  "paymentAmount": {
    "value": "8000",
    "currency": "EUR"
  },
  "paymentCreateTime": "2020-01-01T12:01:00+08:30",
  "paymentTime": "2020-01-01T12:01:01+08:30"
}
  1. 异步通知验签。

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

您需要按照以下方法对 Antom 发送的通知进行验签:

copy
 /**
     * 接收支付通知
     *
     * @param request    request
     * @param notifyBody notify body
     * @return Result
     */
    @PostMapping("/receivePaymentNotify")
    @ResponseBody
    public Result receivePaymentNotify(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");
            }

            // 反序列化通知报文体
            AlipaySubscriptionPayNotify paymentNotify = JSON.parseObject(notifyBody, AlipaySubscriptionPayNotify.class);

            if (paymentNotify != null && "SUCCESS".equals(paymentNotify.getResult().getResultCode())) {
                // 处理你的业务逻辑
                // 例如:通过 subscriptionRequestId 与用户 ID 的关系保存用户的支付信息。
                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. 响应通知结果。无论订单是否支付成功,每个通知请求均需按以下固定格式响应。否则,Antom 会重新发送异步通知。
copy
{
    "result": {
        "resultCode": "SUCCESS",
        "resultStatus": "S",
        "resultMessage": "success"
    }
}

步骤 5:请款并获取请款通知 服务端

注意:只有授权支付成功才会触发请款。

授权支付成功后,Antom 默认自动为您请款,也支持您手动发起请款。同时,Antom 会使用 notifyCapture(单笔支付)将请款结果通知发送给您,您也可以通过主动查询来获取请款结果。您需要根据请款结果来决定是否发货,具体操作请参阅请款

步骤 6:获取订阅通知 服务端

订阅关系生效后,Antom 会为您发送以下通知:

首期订阅通知

Antom 将通过 HTTPS 向您在接口或 Antom Dashboard 中配置的 Webhook 推送以下事件通知:

通知类型

接口

请求报文信息

订阅状态通知

订阅关系生效后,Antom 会使用 notifySubscription口将订阅结果通知发送给您。

  • 请求报文 subscriptionNotificationType 的值为 CREATE 时,subscriptionStatus 判断订阅关系生效 (ACTIVE) 或失效 (TERMINATED)。
  • 请求报文 subscriptionNotificationType 的值为 TERMINATE 时,订阅关系失效。

当期扣款结果通知

订阅支付成功开通或续期成功后,Antom 会使用 notifyPayment(订阅)接口将本期订阅扣款结果发送给您。

请求报文 notifyType 的值为 PAYMENT_RESULTresult.resultStatus 判断本期扣款成功 (S) 还是失败 (F)。

请按以下步骤获取订阅状态通知:

  1. 设置接收通知的 webhook URL:通过 pay(单笔支付)接口的 subscriptionInfo.subscriptionNotifyUrl 参数设置。以下是两种订阅状态的通知示例:
  • 当 subscriptionNotificationType 的值为 CREATE 时,请根据 subscriptionStatus 的值判断订阅关系:
  • ACTIVE:表示订阅关系生效。
  • TERMINATED:表示订阅关系失效。
copy
{
  "periodRule": {
    "periodCount": 1,
    "periodType": "MONTH"
  },
  "subscriptionEndTime": "2074-02-20T01:16:17-08:00",
  "subscriptionId": "2024091******000000050000010226",
  "subscriptionNotificationType": "CREATE",
  "subscriptionRequestId": "SUBSCRIPTION_20244******165oo009851_AUTO",
  "subscriptionStartTime": "2024-09-13T19:30:17-07:00",
  "subscriptionStatus": "ACTIVE"
}
  • 当 subscriptionNotificationType 的值为 TERMINATE 时,订阅关系失效。
copy
{
  "periodRule": {
    "periodCount": 1,
    "periodType": "WEEK"
  },
  "subscriptionId": "2025102619******00000160000671943",
  "subscriptionLastUpdateTime": "2025-10-26T09:51:13-07:00",
  "subscriptionNotificationType": "TERMINATE",
  "subscriptionRequestId": "PR_en_****176",
  "subscriptionStartTime": "2025-10-26T10:01:13-07:00",
  "subscriptionStatus": "TERMINATED"
}
  1. 异步通知验签。

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

您需要按照以下方法对 Antom 发送的通知进行验签:

copy
/**
     * receive subscription notify
     *
     * @param request    request
     * @param notifyBody notify body
     * @return Result
     */
    @PostMapping("/receiveSubscriptionNotify")
    @ResponseBody
    public Result receiveSubscriptionNotify(HttpServletRequest request, @RequestBody String notifyBody) {
        // retrieve the required parameters from http request
        String requestUri = request.getRequestURI();
        String requestMethod = request.getMethod();

        // retrieve the required parameters from request header
        String requestTime = request.getHeader("request-time");
        String clientId = request.getHeader("client-id");
        String signature = request.getHeader("signature");

        try {
            // verify the signature of notification
            boolean verifyResult = WebhookTool.checkSignature(requestUri, requestMethod, clientId,
                    requestTime, signature, notifyBody, ANTOM_PUBLIC_KEY);
            if (!verifyResult) {
                throw new RuntimeException("Invalid notify signature");
            }

            // deserialize the notification body
            AlipaySubscriptionNotify subscriptionNotify = JSON.parseObject(notifyBody, AlipaySubscriptionNotify.class);

            if (subscriptionNotify != null && SubscriptionNotificationType.CREATE.equals(subscriptionNotify.getSubscriptionNotificationType())) {
                // handle your own business logic.
                // e.g. The subscription information of the user is saved through the relationship between the subscriptionRequestId and the user ID.
                System.out.println("receive subscription notify: " + JSON.toJSONString(subscriptionNotify));
                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. 响应通知结果。无论订单是否支付成功,每个通知请求均需按以下固定格式响应。否则,Antom 会重新发送异步通知。
copy
{
    "result": {
        "resultCode": "SUCCESS",
        "resultStatus": "S",
        "resultMessage": "success"
    }
}

常见问题

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

会。对于以下情况,异步通知将在 24 小时内自动重新发送:
  • 由于网络原因未收到异步通知。
  • 如果收到来自 Antom 的异步通知,但您没有按照返回收到确认信息的格式进行响应。

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

问:收到支付结果通知是否需要验签?

答:需要。通过验签 Antom 会发送保障回调请求给您,验签时请注意拼装待验签报文时需按标准处理:<http-method> <http-uri> <client-id>.<request-time>.<request-body>,特别是针对 <request-body> 需直接取值而非解析 JSON 后拼装。

问:若首次支付失败,订阅关系会生效吗?

答:当订阅开通的首次扣款失败时,订阅关系将不会生效,Antom 系统会通过 Webhook 推送 subscriptionStatus TERMINATED 的状态通知给您,表明订阅开通失败。

问:订阅超时时间是多久?

答:针对卡支付类支付方式,默认为 7 天超时时间,您也可以通过 subscriptionExpiryTime 字段来指定超时时间。

问:订阅当期扣款结果通知和授权支付通知、请款通知有什么区别?

答:授权支付阶段通知中包含 3DS 等相关的信息;请款通知则体现最终的支付结果,建议以此通知作为发货依据;订阅当期支付结果通知中包含了订阅周期信息,包含当期的开始,结束时间及期数等。

问:订阅的首次绑定支付支持商户自行发起请款吗?

答:支持,需要在 pay(单笔支付)接口中将字段 paymentFactor.captureMode 的值设置为 MANUAL,然后在收到授权支付成功的异步通知后,自行发起 capture(单笔支付)调用。在此场景下,需保障请款成功才算首次绑定支付成功,Antom 才会生成后续的周期扣款计划。

订阅续期通知

当订阅创建成功并建立有效关系后,Antom 系统将会根据您配置的订阅规则,自动发起续订扣款,并通过 Webhook 推送相应的支付结果通知,实现周期性扣费。触发续订扣款的时间和规则如下:

  • 触发时间:续订扣款将在下一个订阅周期起始日的前 24 小时自动触发。您可以依据上一周期支付结果通知中的 periodEndTime 字段,向前推 24 小时以判断下一周期扣款的发起时间。
  • 周期规则:续费周期及扣款频率将按照订阅时设定的 periodRule 执行,例如按日、按月、按季度或按年扣款。

首期扣款时间

首期 (paymentTime = periodStartTime)

订阅周期

下期扣款时间 (paymentTime)

下期生效时间 (periodStartTime)

2024-09-25T20:10:17+08:00

设置订阅周期为 3 天,需要传入以下参数:

  • periodRule.periodCount = 3
  • periodRule.periodType =DAY

2024-09-27T20:20:04+08:00

2024-09-28T20:10:17+08:00

2024-09-25T20:10:17+08:00

设置订阅周期为 1 周,需要传入以下参数:

  • periodRule.periodCount = 1
  • periodRule.periodType =WEEK

2024-10-01T20:10:17+08:00

2024-10-02T20:10:17+08:00

2024-09-25T20:10:17+08:00

设置订阅周期为 1 个月,需要传入以下参数:

  • periodRule.periodCount = 1
  • periodRule.periodType =MONTH

2024-10-24T20:10:17+08:00

2024-10-25T20:10:17+08:00

2024-09-25T20:10:17+08:00

设置订阅周期为 1 年,需要传入以下参数:

  • periodRule.periodCount = 1
  • periodRule.periodType =YEAR

2025-09-24T20:10:17+08:00

2025-09-25T20:10:17+08:00

异步通知通常分为以下场景:

常见场景

首次绑定支付

后续周期扣款

授权支付结果通知

有通知,告知授权结果和 3DS 认证结果。

有通知,告知授权结果和 3DS 认证结果。

请款通知

授权成功则有通知,请款结果为发货依据。

授权成功则有通知,请款结果为发货依据。

订阅状态通知

有通知,告知订阅创建结果。

无通知。

当期扣费结果通知

有通知,告知本期扣费状态、金额和有效期。

有通知,告知每期扣费状态、金额和有效期

以下为各场景异步通知的示例代码:

copy
{
  "notifyType": "PAYMENT_RESULT",
  "result": {
    "resultCode": "SUCCESS",
    "resultStatus": "S",
    "resultMessage": "success"
  },
  "paymentRequestId": "2020010123456789XXXX",
  "paymentId": "2020010123456789XXXX",
  "paymentAmount": {
    "value": "8000",
    "currency": "EUR"
  },
  "paymentCreateTime": "2020-01-01T12:01:00+08:30",
  "paymentTime": "2020-01-01T12:01:01+08:30"
}

常见问题

问:若扣款失败会导致订阅关系失效吗?

答:创建订阅的首期扣款如果失败,订阅关系将不会生效;若订阅关系已生效但后续的周期扣款失败(如余额不足),订阅状态仍保持有效;若未主动取消订阅,Antom 将会在下一期继续周期扣款。

问:扣款失败会发送当期扣款通知吗,会重试吗?

答:扣款失败会发送扣款失败通知;卡支付的订阅支付场景下,Antom 不会发起重试。商户侧如需自行发起重试,可联系技术支持确定方案.

问:假如首期扣款是 2 月 28 日、3 月 31 日或 4 月 30 日这些月末时间点,那下一期的扣费时间怎么定义?

答:订阅的周期逻辑为按选定的日期来发起下一次扣款,如果下一个周期没有这个日期,则往前推到最后一天,例如:

  • 首期 1.28,二期 2.28,三期 3.28,四期 4.28。
  • 首期 1.31,二期 2.28,三期 3.31,四期 4.30。
  • 首期 1.30,二期 2.28,三期 3.30,四期 4.30。
问:后续的周期扣款如何跟首期合约关联?

答:针对周期扣款通知,可以根据通知请求中的 subscriptionRequestIdsubscriptionId 与首期合约关联。针对卡支付的周期扣款, Antom 还会额外发送授权结果通知和请款结果通知,这两个通知可以根据 paymentId 关联到周期扣款通知中的 paymentId 并最终关联上首期合约的 subscriptionId

问:针对卡支付,如果首次创建订阅关系时选择由商户自行请款,即字段 paymentFactor.captureMode 的值为 MANUAL,那后续的自动续费会如何处理?

答:后续自动续费均由 Antom 发起,无需商户侧参与发起请款,即均默认 paymentFactor.captureMode 的值为 AUTOMATIC

订阅后操作

完成订阅后,您可对交易进行以下操作:

订阅试用

Antom 提供订阅试用功能,帮助买家在正式购买订阅计划前,以免费或优惠价格在限定时间内体验产品或服务。详情请参阅订阅试用文档。

终止订阅

终止订阅功能允许买家在不需要继续使用相关服务时,随时取消当前订阅。详情请参阅终止订阅文档。

取消交易 服务端

对于支付成功后的订单,如买家在当天内申请取消订单或退款,您可以通过 Antom 提供的取消交易能力将订单状态取消或解冻。此外,对于尚未完成支付的订单,您也可以直接进行取消,具体的集成方案见取消交易文档。

退款 服务端

对于已支付成功的订单,如您需向买家发起退款,Antom 提供以下两种方式。

  • 由您的运营人员直接在 Antom Dashboard 平台上进行人工退款。
  • 通过 API 方式接入 refund 接口发起退款。

Antom 的退款能力如下:

  • 支持全额退款。
  • 支持部分多次退款,多次退款的总金额需小于等于请款金额。

具体的集成方案见退款文档。

更多内容

测试卡

使用测试银行卡确保您的集成已准备好投入生产。想要了解更多关于下载测试钱包的信息,请参阅测试资源

最佳实践

为了提高集成效率,Antom 为您提供以下最佳实践方案: