# Payment Element recurring payments

> Follow the guide to integrate Antom Payment Element to accept recurring payments.

 Subscription Payment is a payment solution that supports periodic automatic deductions, helping you easily achieve automated recurring revenue collection. Buyers only need to complete a one-time authorization and binding to continuously enjoy subscription services, while also supporting flexible adjustments to subscription configurations (such as modifying cycles/amounts, canceling renewals, or terminating services). The entire process is secure, reliable, and user-friendly, balancing efficiency with transaction security.

 Antom Payment Element is a payment component integrated via SDK, designed to provide a seamless payment experience and help increase conversion rates. For different client types, Antom offers the following solutions:

 - Web/WAP: Web Element for browser and mobile web environments
- App (Android & iOS): Mobile Element designed for merchants' native mobile applications

 The capabilities supported by Payment Element on each platform are shown in the table below:

 | **Feature** | **Description** | **Web/WAP** | **Android** | **iOS** |
| --- | --- | --- | --- | --- |
| Payment Element-rendered payment method list | You can use the default payment method list rendered by Payment Element on the checkout page. You can also   [specify payment methods](#L9xOX)   to flexibly control which payment methods are displayed. | ✔️ | ✔️ | ✔️ |
| Merchant-rendered payment methods (single-payment-method integration) | You can   [specify individual payment methods](#L9xOX)   according to your business needs to display a customized payment method list. > **[INFO]** **Note**    : For   [certain payment methods](#Ygtd4X)   (e.g., card payments), you need to embed the payment details component rendered by Payment Element. | ✔️ | ✔️ | ✔️ |
| Embedded experience | The payment form is directly embedded on your merchant page, delivering a seamless integration experience. | ✔️ | ❌ | ❌ |
| Pop-up experience | The buyer completes payment in a separate pop-up window after clicking, offering simple integration and secure experience. | ❌ | ✔️ | ✔️ |
| [Appearance customization](https://docs.antom.com/ac/cashierpay/appearance.md) | Use the     *appearance*     parameter to customize the visual style and layout of the payment interface, including theme selection, layout configuration, and CSS styling. | ✔️ | ✔️ | ✔️ |
| Custom p   ayment button | ​   You can customize the payment button. When the buyer clicks your custom payment button, you need to call the   `[submitPayment()](https://docs.antom.com/ac/sdks/web_submitPayment.md)`   method to submit payment. | ✔️ | ❌ | ❌ |

<!-- ToggleTab query="platform" -->

**Tab: Web/WAP**

# User experience {#BEWwv}

 The following figures illustrates the user experience of integrating Payment Element for the first-time subscription and subsequent deductions:

## First-time subscription {#oFdja}

<!-- TabGroup -->

**Tab: APM payments**

The figure below illustrates the buyer's user experience for APM payments:

 ![APM first-time subscription payment flow using Antom Payment Element on Web/WAP](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/0eb92bb8-4146-4868-810e-4c80a9923bad.png)

**Tab: Card payments**

The figure below illustrates the buyer's user experience for card payments:

 ![Card first-time subscription payment flow using Antom Payment Element on Web/WAP](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/4527748c-1387-49d0-9af9-f278d2b0d0cc.png)

<!-- /TabGroup -->

## Subsequent deductions {#F7h4C}

 Antom server will automatically initiate subsequent periodic deductions. The merchant server will receive subscription renewal payment notifications to renew the subscription service for the buyer. This process involves no page interaction.

# Order lifecycle {#iYCQo}

 Learn about the lifecycle of different payment methods:

<!-- TabGroup -->

**Tab: APM Payments**

The following figure illustrates the subscription lifecycle for APM payments, including creating a subscription, signing and binding a payment method, completing the initial deduction, and initiating refunds when necessary. This process ensures the normal activation of subscriptions and secure, transparent handling of fees:

 ![Subscription lifecycle diagram for APM payments](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/428e0e06-2bad-4df7-bb44-e0c6735ff961.png)

**Tab: Card payments, Apple Pay, Google Pay**

The following figure illustrates the subscription lifecycle for card payments, [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), and [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), including creating a subscription, signing and binding a payment method, completing the initial deduction, and initiating refunds when necessary. This process ensures the normal activation of subscriptions and secure, transparent handling of fees:

 ![Subscription lifecycle diagram for card payments, Google Pay, and Apple Pay](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/c83d8e1a-edd1-4583-b156-49012af04486.png)

<!-- /TabGroup -->

 ![Subscription status and notification flow during the subscription lifecycle for Payment Element integration](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/360391dc-b3bd-41d5-80eb-de906fbe694c.png)

# Payment flow {#nigJF}

 The following flow illustrates how to integrate Subscription Payment using Payment Element:

<!-- TabGroup -->

**Tab: APM Payments**

<!-- TabGroup -->

**Tab: First-time subscription**

![APM first-time subscription payment flow using Payment Element](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/4f5c0b70-3ca8-4698-8b46-61a2caaa3b01.png)

**Tab: Subsequent deductions**

![APM subsequent recurring deductions using Payment Element](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/21a235fb-e31a-467b-8a6e-0db536e6767e.png)

<!-- /TabGroup -->

**Tab: Card payments, Apple Pay, Google Pay**

<!-- TabGroup -->

**Tab: First-time subscription**

![Card, Apple Pay, and Google Pay first-time subscription payment flow using Payment Element](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/4c6de5b6-0a7a-4df3-8eb5-0844166371f5.png)

**Tab: Subsequent deductions**

![Card, Apple Pay, and Google Pay subsequent recurring deductions using Payment Element](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/5926a000-fb1c-4fcf-b194-2397b614ee61.png)

<!-- /TabGroup -->

<!-- /TabGroup -->

<!-- TabGroup -->

**Tab: First-time subscription**

1. **The buyer enters the subscription product page and initiates payment.**  
   Merchant client collects relevant information about the buyer's subscription.
2. **Create a payment session request.**  
 Call the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)        API to obtain the payment session. You can specify one or multiple payment method types, or leave the payment method type unspecified, to submit the payment request.
3. **Invoke Payment Element.**  
   On the client side, invoke Payment Element using the payment session. You can choose to use the Payment Element-rendered payment method list, or render the payment methods by specifying them yourself. Payment Element will handle information processing, collect payment details, perform redirects, manage app invocations, display QR codes, and conduct validations based on the features of the selected payment method. After the payment is completed, depending on your configuration and the payment method features, you need to handle redirections based on the result returned by the   `submitPayment().then()`   method, or the system will automatically return to your result page.
4. **Obtain the authorization or payment result.**  
   You can obtain the payment or authorization results through one of the following two methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)      to set the address for receiving asynchronous notifications. When the payment is successful or expires, Antom will use   [**notifyPayment**](https://docs.antom.com/ac/ams/paymentrn_online.md)   to send asynchronous notifications to you.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the payment or authorization status.

 > **[INFO]** **Note**    : For card payments and certain APM payment methods (such as [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Pay by Bank](https://docs.antom.com/ac/antomop/pay_by_bank.md)), these payment methods follow an authorized-capture mode. The steps above only complete the authorization phase, indicates that the buyer has completed the payment using their card, and the funds are in a frozen state. To transfer the buyer's frozen funds to your account, you must integrate the capture step. The successful capture result will serve as the basis for shipping goods.

 5. **(Optional) Capture and obtain the capture result.**

 By default, Antom automatically handles fund capture on your behalf. You can also manually capture funds by calling the   [**capture (One-time Payments)**](https://docs.antom.com/ac/ams/capture.md)   API. The capture result can be obtained through one of the following methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)   to set the address for receiving asynchronous notifications. Upon capture completion, Antom will send you asynchronous notifications via the   [**notifyCapture (One-time Payments)**](https://docs.antom.com/ac/ams/notify_capture.md)   API.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the capture status.

 6. **Obtain subscription notifications.**  
   Once the subscription is activated, Antom will send initial subscription notifications and subscription renewal notifications to you.

**Tab: Subsequent deductions**

1. **Antom server initiates a deduction from the payment method.**
2. **Obtain the authorization or payment result.**  
   You can obtain the payment or authorization results through one of the following two methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)      to set the address for receiving asynchronous notifications. When the payment is successful or expires, Antom will use   [**notifyPayment**](https://docs.antom.com/ac/ams/paymentrn_online.md)   to send asynchronous notifications to you.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the payment or authorization status.

 > **[INFO]** **Note**    : For card payments and certain APM payment methods (such as [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Pay by Bank](https://docs.antom.com/ac/antomop/pay_by_bank.md)), these payment methods follow an authorized-capture mode. The steps above only complete the authorization phase, indicates that the buyer has completed the payment using their card, and the funds are in a frozen state. To transfer the buyer's frozen funds to your account, you must integrate the capture step. The successful capture result will serve as the basis for shipping goods.

 3. **(Optional) Capture and obtain the capture result.**

 For subsequent deductions, Antom automatically handles fund capture on your behalf. You can obtain the capture result through one of the following methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)   to set the address for receiving asynchronous notifications. Upon capture completion, Antom will send you asynchronous notifications via the   [**notifyCapture (One-time Payments)**](https://docs.antom.com/ac/ams/notify_capture.md)   API.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the capture status.

 4. **Obtain subscription notifications.**  
   After the deduction is successful or fails, Antom will send you a subscription deduction notification.

<!-- /TabGroup -->

# Integration preparations {#hKAW}

 Before you start integrating, read   [Integration guide](https://docs.antom.com/integration_guide_en.md)   and   [API overview](https://docs.antom.com/ac/ams/api_fund.md)   to understand the integration steps of the server-side API and the precautions for calling the API. Furthermore, ensure the following prerequisites are met:

 - Obtained your client ID
- Complete the key configuration
- Complete the configuration of     *paymentNotifyUrl*     to receive the asynchronous notification
- Integrate the server-side SDK package, install the server-side library, and initialize a request instance. For more details, refer to   [Server-side SDKs](https://docs.antom.com/ac/sdks/server_sdks.md)  .
- Integrate the client-side SDK package by following the steps detailed in   [Integrate the SDK package for Web/WAP](https://docs.antom.com/ac/sdks/web.md)  , and ensure to use the latest SDK version or no lower than 1.46.0.

# Integration steps {#n1t0Y}

 Start your integration by taking the following steps:

 1. Create a payment session
2. Invoke Payment Element
3. Obtain the authorization or payment result
4. (Optional) Capture
5. Obtain subscription notifications

## Step 1: Create a payment session   **[Server-side]** {#KuWal}

 Call the      [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API with order information to create a payment session and obtain the     *paymentSessionData*     required to invoke Payment Element. You can choose to either    render the payment methods by specifying them yourself    or    use the Payment Element-rendered payment method list   . Pass the corresponding parameters when calling the      [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API:

 - Render the payment methods by yourself:    When rendering the payment method list yourself, you must pass the parameters for specifying payment methods listed in the table below. Note that for   [certain payment methods](#Ygtd4X)   (e.g., card payments), you need to embed the payment details component rendered by Payment Element. The optimal timing for calling the      [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API when offering card payment options is as follows:

   - If Payment Element needs to collect payment details: Call the      [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API after the buyer selects the payment method, and pass the card payment parameters listed in the table.
  - If Payment Element does not need to collect payment details: Call the      [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API after the buyer selects the payment method and submits payment.

 - When using the Payment    Element-rendered    payment method list, you only need to pass the basic parameters listed in the table below. Payment Element renders all supported payment methods on the checkout page by default, but you can [specify payment methods](#L9xOX) to display only the options you need.

   | **Type** | **Parameter name** | **Required** | **Description** |
| --- | --- | --- | --- |
| Basic parameters ​ | *productCode* | Yes | For    Payment    Element   , the value is fixed as      `CASHIER_PAYMENT`  . |
| *productScene* | Yes | For    Payment    Element   , the value is fixed as      `ELEMENT_PAYMENT`  . |  |
| *paymentRequestId* | Yes | The unique ID assigned by the merchant to identify a payment request. |  |
| *paymentAmount* | Yes | The payment amount that the merchant requests to receive in the order currency. |  |
| *paymentRedirectUrl* | Yes | ​   The merchant page URL that the buyer is redirected to after the payment is completed. |  |
| *paymentNotifyUrl* | No | ​   The payment result notification URL, which can be passed in through the API or set as a fixed value through Antom Dashboard. If both are set, the value in the API takes precedence. |  |
| *settlementStrategy* | Yes | The settlement strategy for the payment request. |  |
| *order.referenceOrderId* | Yes | The unique ID to identify the order on the merchant side. |  |
| *order.orderDescription* | Yes | Summary description of the order. |  |
| *env.terminalType* | No | Terminal type of which the merchant service applies to. Valid values are: - `WEB`  : The client-side terminal type is a website, which is opened via a PC browser. - `WAP`  : The client-side terminal type is an H5 page, which is opened via a mobile browser. - `APP`  : The client-side terminal type is a mobile application.   > **[INFO]** **Note**    : This parameter is required for [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md). |  |
| *env.osType* | No | The operating system type from which the buyer initiated the transaction. Valid values: - `IOS`  : Apple's iOS. - `ANDROID`  : Google's Android.   > **[INFO]** **Note**    : Specify this parameter when     *env.TerminalType*     is   `WAP`   or   `APP`  . |  |
| *env.clientIp* | No | The IP address of the client device. > **[INFO]** **Note**    : This parameter is required for card payments, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md). |  |
| *subscriptionInfo.subscriptionDescription* | Yes | The description of the subscription. |  |
| *subscriptionInfo.subscriptionStartTime* | Yes | The date and time when the subscription becomes active. |  |
| *subscriptionInfo.subscriptionNotifyUrl* | Yes | The URL that is used to receive the subscription result notification. HTTPS address must be provided. |  |
| *subscriptionInfo.subscriptionExpiryTime* | No | A specific date and time after which the creation of a subscription order will expire. After the subscription expires, the order must be terminated. Default values for this parameter: - Card payments, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md): 7 days (calculated from the time the subscription creation request is sent). - APM payments: 80 minutes (calculated from the time the subscription creation request is sent).   > **[INFO]** **Note**    : For APM payment methods, you need to set the subscription expiration time to greater than 74 minutes. |  |
| *subscriptionInfo.periodRule* | Yes | The subscription period type, supporting four types of cycles:   `YEAR`  ,   `MONTH`  ,   `WEEK`  ,   `DAY`  . |  |
| Card payment parameters (Refer to   [Card payment features](#abqU1)  ) | *paymentFactor.captureMode* | No | Indicates the method for capturing funds after the user authorizes the payment. Valid values are: - `AUTOMATIC`  : indicates that Antom automatically captures the funds after the authorization. The same applies when the value is empty or you do not pass in this parameter. - `MANUAL`  : indicates that you manually capture the funds by calling the   [**capture (Checkout Payment)**](https://docs.antom.com/ac/ams/capture.md)   API. |
| *order.buyer* | Yes ​ | Buyer information. At least one of the following must be provided: - *order.buyer.referenceBuyerId* - *order.buyer.buyerPhoneNo* - *order.buyer.buyerEmail* |  |
| *availablePaymentMethod.paymentMethodMetaData.is3DSAuthentication* | No | Determine whether to set the transaction authentication type to 3DS based on risk and dispute history. Valid values are: - `true`  :    indicates that the transaction authentication type is    3DS authentication. - `false`  :    indicates that the transaction authentication type is    Non-3DS authentication. Default if empty or not provided. |  |
| *availablePaymentMethod.paymentMethodMetaData.tokenizeMode* | No | The tokenize mode. Indicates whether card information needs to be stored. When you opt to   [store card details](https://docs.antom.com/ac/pm/tokenization.md)  , the actual information is replaced with a unique and secure token that can be used for future payments. Valid values are: - `ENABLED`  : indicates that you obtain card binding authorization and want to store card information for future payments. - `DISABLED`  : i   ndicates that you don’t need to store card information. The same applies when the value is empty or you do not specify this parameter. - `ASKFORCONSENT`  : indicates that you present a button on the page, allowing the buyer to choose whether or not they want to store the card information. |  |
| *availablePaymentMethod.paymentMethodMetaData.isCardOnFile* | No | Indicates whether the card used in this transaction has been   [previously processed and stored](https://docs.antom.com/ac/pm/cof.md)   by the merchant. Valid values are: - `true`  : The card used in the transaction has been previously processed and stored by the merchant. Set to   `true`   for stored card payments. - `false`  : Default. The card is new and has not been used previously on the merchant side. |  |
| *savedPaymentMethods.paymentMethodId* | No | Used for stored card payments.    The value of this parameter is that of     *cardToken*     obtained from the new card payment. |  |
| Parameters for specifying payment methods (Refer to   [Specify payment methods](#L9xOX)  ) | *availablePaymentMethod.paymentMethodTypeList.paymentMethodType* | No | Used to specify payment methods. You can specify one or multiple payment methods. |
| *availablePaymentMethod.paymentMethodTypeList.paymentMethodOrder* | No | Set the priority order of payment methods. |  |

 The above parameters are the basic parameters for creating a payment session, for full parameters and additional requirements for certain payment methods refer to   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   **.**

 The following sample code shows how to call the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)        API:

 ```java
public ResponseEntity<ApiResponse> createPaymentSession(@RequestBody PaymentVO payment) {
    AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
    alipayPaymentSessionRequest.setProductCode(ProductCodeType.CASHIER_PAYMENT);
    alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.ELEMENT_PAYMENT);

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

    // convert amount unit(in practice, amount should be calculated on your serverside)
    // For details, please refer to: <a href="https://docs.antom.com/ac/ref/cc">Usage rules of the Amount object</a>
    long amountMinorLong = Money.of(CurrencyUnit.of(payment.currency), new BigDecimal(payment.amountValue)).getAmountMinorLong();

    // set amount
    Amount amount = Amount.builder().currency(payment.currency).value(String.valueOf(amountMinorLong)).build();
    alipayPaymentSessionRequest.setPaymentAmount(amount);

    // set settlement strategy
    // replace with your existing settlement currency
    SettlementStrategy settlementStrategy = SettlementStrategy.builder().settlementCurrency("SGD").build();
    alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

    // 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 sdk testing order").orderAmount(amount).buyer(buyer).build();
    alipayPaymentSessionRequest.setOrder(order);

    // replace with your notify url
    // or configure your notify url here: <a href="https://dashboard.antom.com/global-payments/developers/iNotify">Notification URL</a>
    alipayPaymentSessionRequest.setPaymentNotifyUrl("http://www.yourNotifyUrl.com/payment/receivePaymentNotify");

    // replace with your redirect url
    alipayPaymentSessionRequest.setPaymentRedirectUrl(
        "http://localhost:8080/index.html?paymentRequestId=" + paymentRequestId);

    // set subscription info
    PeriodRule periodRule = PeriodRule.builder().periodCount(1).
        periodType("MONTH").build();

    List trials = new ArrayList<Trial>();

    Trial trial = Trial.builder().
        trialAmount(amount).
        trialStartPeriod(1).
        trialEndPeriod(2).
        build();

    trials.add(trial);

    SubscriptionInfo subscriptionInfo = SubscriptionInfo.builder().
        subscriptionDescription("Subscription description").
        subscriptionStartTime("2026-03-11T09:48:17+08:00").
        subscriptionEndTime("2026-11-21T09:48:17+08:00").
        periodRule(periodRule).
        trials(trials).
        subscriptionNotifyUrl("https://your.example.com/subscriptionNotify").
        subscriptionExpiryTime("2026-03-12T09:48:17+08:00").
        build();
    alipayPaymentSessionRequest.setSubscriptionInfo(subscriptionInfo);

    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));
}
```

<!-- TabGroup -->

**Tab: Merchant-rendered payment method list**

If you render the payment method list yourself, you must integrate by specifying individual payment methods. The following code shows a sample of the request message:

 ```json
{
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "USD",
      "value": "2900"
    },
    "orderDescription": "antom sdk testing order",
    "referenceOrderId": "4b085ec4-9999-4296-8f00-479e929edb2c"
  },
  "paymentAmount": {
    "currency": "USD",
    "value": "2900"
  },
  "paymentNotifyUrl": "http://www.yourNotifyUrl.com/payment/receivePaymentNotify",
  "paymentRedirectUrl": "http://google.com.my",
  "paymentRequestId": "PAYMENT_20260313102551842_AUTO",
  "productCode": "CASHIER_PAYMENT",
  "productScene": "ELEMENT_PAYMENT",
  "settlementStrategy": {
    "settlementCurrency": "USD"
  },

  "availablePaymentMethod": {
    "paymentMethodTypeList": [

      {
        "paymentMethodType": "ALIPAY_CN" // Specify payment method
      },

      {
        "paymentMethodType": "TNG"
      }

    ]
  },

  "subscriptionInfo": {
    "periodRule": {
      "periodCount": 1,
      "periodType": "MONTH"
    },
    "subscriptionDescription": "Subscription description",
    "subscriptionNotifyUrl": "https://your.example.com/subscriptionNotify",
    "subscriptionStartTime": "2026-03-11T09:48:17+08:00"

  }
}
```

**Tab: Payment Element-rendered payment method list**

If you use Payment Element-rendered payment method list, Payment Element will display all supported payment methods by default. The following code shows a sample of the request message:

 ```json
{
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "SGD",
      "value": "2900"
    },
    "orderDescription": "antom sdk testing order",
    "referenceOrderId": "4b085ec4-9999-4296-8f00-479e929edb2c"
  },
  "paymentAmount": {
    "currency": "SGD",
    "value": "2900"
  },
  "paymentNotifyUrl": "http://www.yourNotifyUrl.com/payment/receivePaymentNotify",
  "paymentRedirectUrl": "http://localhost:8080/index.html?paymentRequestId=7a6252b5-6d58-4033-882e-2eff7f262b35",
  "paymentRequestId": "7a6252b5-6d58-4033-882e-2eff7f262b35",
  "productCode": "CASHIER_PAYMENT",
  "productScene": "ELEMENT_PAYMENT",
  "settlementStrategy": {
    "settlementCurrency": "SGD"
  },
  "subscriptionInfo": {
    "periodRule": {
      "periodCount": 1,
      "periodType": "MONTH"
    },
    "subscriptionDescription": "Subscription description",
    "subscriptionEndTime": "2026-11-21T09:48:17+08:00",
    "subscriptionExpiryTime": "2026-03-12T09:48:17+08:00",
    "subscriptionNotifyUrl": "https://your.example.com/subscriptionNotify",
    "subscriptionStartTime": "2026-03-11T09:48:17+08:00",
    "trials": [
      {
        "trialAmount": {
          "$ref": "$.order.orderAmount"
        },
        "trialEndPeriod": 2,
        "trialStartPeriod": 1
      }
    ]
  }
}
```

<!-- /TabGroup -->
 The following code shows a sample of the response, which contains the following parameters:

 - *result.resultStatus*    : The result of the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API call.
- *paymentSessionData*    : The payment session data to be returned to the client.
- *paymentSessionExpiryTime*    : The expiration time of the payment session.

 ```json
{
  "paymentSessionData": "WNoudiUNUDjHjx0oXddzWn0QrRHv52lrHFNMKc8/5uKSqNDklmQ7Nh4JGBNITqi3jgIASZkbpq15gIEQleY13A==&&SG&&188&&eyJhY3Rpb24iOnsibmVlZFZlcmlmeUFuZFJlc3VtZSI6ZmFsc2UsInNpZ25CdXR0b25EaXNwbGF5IjpmYWxzZSwic2tpcFNka1F1ZXJ5IjpmYWxzZSwidXNlclNpZ25BZ3JlZW1lbnQiOmZhbHNlfSwiY2xpZW50SWQiOiJTQU5EQk9YXzVZRVYxRzJaVjQzSzA3MjE3IiwiY29ubmVjdEZhY3RvciI6eyJlbmFibGVDb25uZWN0IjpmYWxzZX0sImVsaWdpYmxlRWFzeVBheU1hcmtldGluZyI6ZmFsc2UsImV4dGVuZEluZm8iOiJ7XCJPUEVOX01VTFRJX1BBWU1FTlRfQUJJTElUWVwiOlwidHJ1ZVwiLFwiZXhwcmVzc0NoZWNrb3V0XCI6XCJmYWxzZVwiLFwidmVyc2lvbk1hcFwiOlwie1xcXCJ3ZWJcXFwiOntcXFwiMS4xLjBcXFwiOntcXFwidGFyZ2V0V2ViVmVyaXNvblxcXCI6XFxcIjEuMS4wXFxcIn0sXFxcIjEuMi4wXFxcIjp7XFxcInRhcmdldFdlYlZlcmlzb25cXFwiOlxcXCIxLjIuMFxcXCJ9fSxcXFwiaU9TXFxcIjp7XFxcIjEuMS4wXFxcIjp7XFxcInRhcmdldFdlYlZlcmlzb25cXFwiOlxcXCIxLjEuMFxcXCJ9LFxcXCIxLjIuMFxcXCI6e1xcXCJ0YXJnZXRXZWJWZXJpc29uXFxcIjpcXFwiMS4yLjBcXFwifX0sXFxcIkFuZHJvaWRcXFwiOntcXFwiMS4xLjBcXFwiOntcXFwidGFyZ2V0V2ViVmVyaXNvblxcXCI6XFxcIjEuMS4wXFxcIn0sXFxcIjEuMi4wXFxcIjp7XFxcInRhcmdldFdlYlZlcmlzb25cXFwiOlxcXCIxLjIuMFxcXCJ9fX1cIn0iLCJuZWVkQWNjb3VudENvbmZpcm1QYWdlIjpmYWxzZSwicGF5bWVudFNlc3Npb25Db25maWciOnsicGF5bWVudE1ldGhvZENhdGVnb3J5VHlwZSI6IkFMTCIsInByb2R1Y3RTY2VuZSI6IkVMRU1FTlRfUEFZTUVOVCIsInByb2R1Y3RTY2VuZVZlcnNpb24iOiIxLjAifSwic2tpcFJlbmRlclBheW1lbnRNZXRob2QiOmZhbHNlfQ==",
  "paymentSessionExpiryTime": "2026-03-12T15:24:24+08:00",
  "paymentSessionId": "WNoudiUNUDjHjx0oXddzWn0QrRHv52lrHFNMKc8/5uIsshVVNylGbBzLF2n6JmoT",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
```

 The table below shows the possible values of     *result.resultStatus*     in the response. Please handle the result according to the guidance provided:

   | ***result.resultStatus*** | **Message** | **Further action** |
| --- | --- | --- |
| `S` | The payment session was successfully created, and     *paymentSessionData*     is returned. | Proceed to the next steps. |
| `U` | Unknown error. | Please use a new          *paymentRequestId*     and call the API again. If the issue persists, contact Antom Technical Support. |
| `F` | Failed to create the payment session. | Please check and verify whether the current API required request fields (including header fields and body fields) are correctly passed and valid. |

 > **[INFO]** **Note**    : If no response is received, it may indicate a network timeout. Please use a new     *paymentRequestId*     and call the API again. If the issue persists, contact Antom Technical Support.

 > **[INFO]** **Common questions**
>
>  **Q: Can I use Chinese characters in the value of the request parameters?**
>
>  A: To avoid incompatibility of certain payment methods, do not use Chinese characters for fields in the request.
>
>  ​
>
>  **Q: How to set the address to receive payment notification?**
>
>  A: Specify     *paymentNotifyUrl*     in the      [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API to receive the asynchronous notification about the payment result (  [**notifyPayment**](https://docs.antom.com/docs/ac/ams/paymentrn_online.md)  ), or configure the receiving URL in Antom Dashboard. If the URL is specified in both the request and Antom Dashboard, the value specified in the request takes precedence.
>
>  ​
>
>  **Q: Does the returned**       ***paymentSessionData***       **require processing before passing it to the client?**
>
>  A: Do not process or modify     *paymentSessionData*     in any way, as this may cause the Payment Element invocation to fail.
>
>  
>  **Q: Is 3DS authentication mandatory for the first transaction?**
>
>  A: The first transaction requires buyer participation and identity verification to ensure the security of subsequent recurring payments (where the buyer is not present). Specific requirements are as follows:
>
>  - Buyer uses card payment: Whether you or Antom collects card information, 3DS authentication must be completed. You can set     *is3DSAuthentication*     to   `true`   in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   request to enable Antom 3DS authentication.
> - Buyer uses [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md) and Antom decrypts the data: You need to set     *is3DSAuthentication*     to   `true`   to initiate a 3DS transaction for identity verification. If the decrypted data is a DPAN (Device Primary Account Number), the system will automatically proceed with the authorization process without requiring additional buyer verification.
> - Buyer uses [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and Antom decrypts the data: You can set     *is3DSAuthentication*     to   `false`   or omit this parameter, as the buyer has already completed Apple's verification process.

## Step 2: Invoke Payment Element   **[Client-side]** {#FWfpW}

 Use     *paymentSessionData*     to invoke Payment Element on your client. After the buyer clicks to submit payment, Payment Element will handle the entire flow based on the selected payment method, including displaying QR codes, redirecting to payment pages, performing 3DS authentication, and returning to the merchant’s result page.

 Depending on the rendering method you chose in Step 1 (merchant-rendered or Payment Element-rendered payment method list), the following table compares how to invoke Payment Element in different scenarios:

 | **Method** | **Integration steps** |  |
| --- | --- | --- |
| Merchant-rendered payment method list       (Single payment method integration) | Payment Element collects payment details   ​ | 1. Call the   `[mount()](https://docs.antom.com/ac/sdks/web_mount.md)`   method to embed the payment details component rendered by Payment Element. 2. After the buyer clicks your custom payment button, call the   `[submitPayment()](https://docs.antom.com/ac/sdks/web_submitPayment.md)`   method to submit payment. |
| Payment Element does not collect payment details   ​ | Call the   `[mount()](https://docs.antom.com/ac/sdks/web_mount.md)`   method without embedding the payment details component. After the buyer clicks your custom payment button, call the   `[submitPayment()](https://docs.antom.com/ac/sdks/web_submitPayment.md)`   method to submit payment directly. |  |
| Payment Element-rendered payment method list | 1. Call the   `[mount()](https://docs.antom.com/ac/sdks/web_mount.md)`   method to embed the Payment Element-rendered payment method list. 2. After the buyer clicks your custom payment button, call the   `[submitPayment()](https://docs.antom.com/ac/sdks/web_submitPayment.md)`   method to submit payment. |  |

 1. After obtaining     *paymentSessionData*     from the server, use the   `[AMSElement](https://docs.antom.com/ac/sdks/web_AMSElement.md)`   class to create a Payment Element instance. The following sample code how to instantiate the SDK using CDN or npm:

<!-- TabGroup -->

**Tab: CDN**

```javascript
// Get the browser language
let language = navigator.language || navigator.userLanguage;
language = language.replace("-", "_"); // Replace "-" with "_"

// Create Payment Element instance
const elementPayment = new window.AMSElement({
  environment: "sandbox",
  locale: "en_US",
  sessionData:sessionData
})
```

**Tab: npm**

```javascript
import { AMSElement, ThemeType, PaymentElementLayout } from '@alipay/ams-checkout' // Package management

// Get the browser language
let language = navigator.language || navigator.userLanguage;
language = language.replace("-", "_"); // Replace "-" with "_"

// Create Payment Element instance
const elementPayment = new AMSElement({
  environment: "sandbox",
  locale: "en_US",
  sessionData:sessionData
})
```

<!-- /TabGroup -->
   2. Use the   `[mount()](https://docs.antom.com/ac/sdks/web_mount.md)`   method from the instance object to create the payment component, and embed the component into the specified view if needed. Before calling the   `[mount()](https://docs.antom.com/ac/sdks/web_mount.md)`   method, you may add a     *loading*     indicator and close it when handling the callback in the   `.then()`   method. If the callback result contains error information, determine the specific error type via     *error?.code*    , and refer to the   [callback function event codes](https://docs.antom.com/ac/sdks/ref.md#M3TUS)   for detailed error causes and handling suggestions. If no error information is present, the   `[mount()](https://docs.antom.com/ac/sdks/web_mount.md)`   method rendering was successful.

 > **[INFO]** **Note**    :
>
>  - If you need to embed the payment details collection component (see   [Payment methods requiring embedding](#Ygtd4X)   for details), it is recommended that the container for the payment element have a minimum width of 375 px and no height restriction, allowing the Payment Element to automatically expand the container height.
> - The different configuration of     *notRedirectAfterComplete*     in the   `[mount()](https://docs.antom.com/ac/sdks/web_mount.md)`   method handles the redirection of the merchant page differently. For details, refer to   [Redirect to the merchant page](https://docs.antom.com/ac/sdks/web_mount.md#gxPhN)  .

 The following sample code shows how to create a payment component using the      `[mount()](https://docs.antom.com/ac/sdks/web_mount.md)`      method:

 ```javascript
let loading = false;

// Customize appearance
const appearance = {
  theme: "default",
  layout: { type: "Accordion" },
  variables: {},
};

// Set external container loading state before calling mount
loading = true;

// Embed into the document.querySelector("#payment-element") node
elementPayment.mount(
  {
    type: 'payment',
    appearance: appearance,
    notRedirectAfterComplete: false,
  },
  '#payment-element',
).then(({ error }) => {
  // Manually turn off external container loading state
  loading = false;

  // Handle error information, process exceptions based on error.code
  if (error && error?.code) {
    // PARAM_INVALID: SDK parameter exception, recommend checking integration code
    // UI_STATE_ERROR: mount call timing exception, recommend checking integration code
    console.log(error.message);
  
    if(error?.code === 'INITIALIZE_API_TIMEOUT') {
    // Payment information query API timeout causing checkout rendering failure, recommend guiding user to retry
  
    } else if(error?.code === 'INITIALIZE_WEB_TIMEOUT'){
      // Checkout static resource loading timeout, recommend guiding user to retry
  
    } ...
    return;
  }
  // mount rendering successful, no handling needed
})
```

 3. After the buyer clicks your custom payment button, call the   `[submitPayment()](https://docs.antom.com/ac/sdks/web_submitPayment.md)`  method to submit payment.
   Before calling the   `[submitPayment()](https://docs.antom.com/ac/sdks/web_submitPayment.md)`   method, you may add a     *loading*     indicator and close it when handling the callback in the   `.then()`   method. It is recommended to display the *loading* state after the buyer clicks the payment button to prevent repeated submissions within a short period. After receiving the payment result returned by Payment Element, guide the buyer to either retry the payment or redirect to the payment result page based on the actual outcome.

   - If the callback result contains error information, you can simplify integration based on the     *status*     value. Refer to the sample code below for specific operations. You may also refer to     *error?.code*     for specific exception handling and refer to   [callback function event codes](https://docs.antom.com/ac/sdks/ref.md#heMi7)   for detailed error causes and handling suggestions.
  - If no error information exists, proceed with subsequent operations based on the     *status*     value.

 ```javascript
let loading = false;

loading = true;
// When the buyer clicks the payment button:
elementPayment.submitPayment().then(({ error, status, userCanceled3D }) => {
  // Manually turn off the external container's loading state
  loading = false;

  if (error) { // Handle error information first
    const { code, message } = error;

    if (userCanceled3D) {
      // Buyer actively closed the 3D popup, recommend polling results from the server
    }

    if (status === 'PROCESSING') {
      // Status unknown due to network exception or channel instability. Recommend polling results from the server
    } else {
      // FAIL - Failure
      // Form validation failed, Payment Element has prompted the buyer, recommend ignoring
      if (code === 'FORM_INVALID') {
        return;
      }

      toast(message);

      // Or customize experience based on code granularity
      if (code ){
        // xxx
      }
    }
    return;
  }

  if (status === 'SUCCESS') {
    // SUCCESS - Success. In some scenarios Payment Element will show a toast prompt, recommend directly redirecting to the result page

  } else if(status === 'CANCELLED'){
    // Order has been cancelled, can be ignored if not applicable to this scenario

  } else {
    // You can add monitoring
  }
})

```

### Unmount Payment Element {#gn3NV8}

 Call the   `[destroy()](https://docs.antom.com/ac/sdks/web_destroy.md)`   method to release the resources occupied by the Payment Element component:

   ```typescript
elementPayment.destroy();
```

   > **[INFO]** **Common questions**
>
>  **Q: Can I integrate the Web Element using Webview in PC or mobile applications?**
>
>  A: Currently not supported.

## Step 3: Obtain the authorization or payment result   **[Server-side]** {#MxqX3}

 In the payment processing flow, Antom will send you corresponding result notifications based on the type of payment method used.

 - For card payments, [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), and [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), Antom sends authorization result notifications to inform you whether the authorization was successful. Only after a successful authorization will the fund capture be triggered. Please use the capture result as the basis for shipping goods.
- For APM payments, when the payment succeeds or fails, Antom will send you payment result notifications.

 You can obtain payment or authorization results by receiving asynchronous notifications from Antom or by proactively querying.

<!-- TabGroup -->

**Tab: Receive asynchronous notifications**

**1\. Configure the webhook URL to receive asynchronous notifications**

 When a payment succeeds or fails, Antom will send an asynchronous notification to the webhook URL you set. You can choose one of the following two methods to configure the webhook URL for receiving notifications (if both are set, the URL specified in the request takes precedence):

 - If each of your orders has a unique notification URL, it is recommended to set the webhook URL in each request. You can pass the asynchronous notification receiving URL for the specific order through     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API.
- If all your orders share a unified notification URL, you can set the webhook URL on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/developers/iNotify)   through     **Developer > Notification URL**    . For detailed steps, refer to   [Notification URL](https://docs.antom.com/ac/merchant_service/notification.md)  .

 The following code shows a sample of the asynchronous notification request:

<!-- TabGroup -->

**Tab: Card payments, Apple Pay, Google Pay**

```json
{
  "actualPaymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "cardInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cardToken":"exxxxe",
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "networkTransactionId": "XXXXX",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "cavv": "",
      "eci": ""
    }
  },
  "notifyType": "PAYMENT_RESULT",
  "paymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "paymentMethodType": "CARD",
  "paymentCreateTime": "2024-01-01T00:00:00+08:00",
  "paymentId": "20240101123456789XXXX",
  "paymentRequestId": "paymentRequestId01",
  "paymentResultInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cardToken":"exxxxe", // store cardToken for future card payments
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "networkTransactionId": "XXXXX",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "cavv": "",
      "eci": ""
    }
  },
  "paymentTime": "2024-01-01T00:01:00+08:00",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
```

 The following table shows the possible values of     *result.resultStatus*     in the notification request of payment result. Please handle the result according to the guidance provided:

   | ***result.resultStatus*** | **Message** | **Further action** |
| --- | --- | --- |
| `S` | Authorization    was successful. | You may proceed to initiate a capture. |
| `F` | Authorization    failed. | Please use a new     *paymentRequestId*        and initiate the payment again. |

**Tab: APM payments**

```json
{
    "actualPaymentAmount": {
        "currency": "HKD",
        "value": "100"
    },
    "notifyType": "PAYMENT_RESULT",
    "paymentAmount": {
        "currency": "HKD",
        "value": "100"
    },
    "paymentCreateTime": "2025-02-04T22:11:19-08:00",
    "paymentId": "20240101123456789XXXX",
    "paymentMethodType": "ALIPAY_HK",
    "paymentRequestId": "paymentRequestId01",
    "paymentResultInfo": {

    },
    "paymentTime": "2025-02-04T22:14:25-08:00",
    "pspCustomerInfo": {
        "pspCustomerId": "216022003753XXXX",
        "pspName": "ALIPAY_HK"
    },
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}
```

 The following table shows the possible values of     *result.resultStatus*     in the notification request of payment result. Please handle the result according to the guidance provided:

 | ***result.resultStatus*** | **Message** | **Further action** |
| --- | --- | --- |
| `S` | Payment was successful. | **​**    You may proceed to ship the goods. |
| `F` | Payment failed. | Please use a new     *paymentRequestId*        and initiate the payment again. |

<!-- /TabGroup -->
 **2\. Verify the asynchronous notification**

 When you receive an asynchronous notification from Antom, you are required to return the response in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)   format, but you do not need to countersign the response.

 You need to verify the signature of the payment notification sent by Antom:

 ```java
import javax.servlet.http.HttpServletRequest;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.alipay.global.api.model.Result;
import com.alipay.global.api.model.ResultStatusType;
import com.alipay.global.api.response.AlipayResponse;
import com.alipay.global.api.tools.WebhookTool;

@RestController
public class PaymentNotifyHandleBySDK {

    /**
     * alipay public key, used to verify signature
     */
    private static final String SERVER_PUBLIC_KEY = "";
  
    /**
     * payment result notify processor
     * using <a href="https://spring.io">Spring Framework</a>
     *
     * @param request    HttpServletRequest
     * @param notifyBody notify body
     * @return
     */
    @PostMapping("/payNotify")
    public Object payNotifyHandler(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");

        Result result;
        AlipayResponse response = new AlipayResponse();

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

            // deserialize the notification body
  
            // update the order status with notify result

            // respond the server that the notification is received
            result = new Result("SUCCESS", "success", ResultStatusType.S);

        } catch (Exception e) {
            String errorMsg = e.getMessage();
            // handle error condition
            result = new Result("ERROR", errorMsg, ResultStatusType.F);
        }
        response.setResult(result);
        return ResponseEntity.ok().body(response);
    }

}
```

 Whether the payment is successful or not, each notification request must be responded to in the format specified below. Otherwise, Antom will resend the asynchronous notification.

 ```json
{
    "result": {
        "resultCode": "SUCCESS",
        "resultStatus": "S",
        "resultMessage": "success"
    }
}
```

 > **[INFO]** **Common questions**
>
>  **Q: When will the notification be sent?**
>
>  A: It depends on whether the payment is completed:
>
>  - If the payment is successfully completed, Antom will send you an asynchronous notification within 3 to 5 seconds. For some payment methods like OTC, the notification might take a bit longer.
> - If the payment is not completed, Antom needs to close the order first before sending an asynchronous notification. The time it takes for different payment methods to close the order varies, usually defaulting to 14 minutes.
>
>  
>  **Q: Will the asynchronous notification be re-sent?**
>
>  A: Yes, the asynchronous notification will be re-sent automatically within 24 hours for the following cases:
>
>  - If you didn't receive the asynchronous notification due to network reasons.
> - If you receive an asynchronous notification from Antom, but you did not respond to the notification in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)      format.
>
>  The notification can be resent up to 8 times or until a correct response is received to terminate delivery. The sending intervals are as follows: 0 minutes, 2 minutes, 10 minutes, 10 minutes, 1 hour, 2 hours, 6 hours, and 15 hours.
>
>  ​
>
>  **Q: When responding to an asynchronous notification, do I need to add a digital signature?**
>
>  A: If you receive an asynchronous notification from Antom, you are required to return the response in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)   format, but you do not need to countersign the response.
>
>  ​
>
>  **Q: What key parameters do I need to use in the notification?**  
>    A: Please note the following key parameters:
>
>  - *result*    : For APM payments, it represents the final payment result. For [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), and card payments, it only represents the authorization result, and further capture is required.
> - *paymentRequestId*    : The payment request ID used for inquiries, cancellations, and reconciliation.
> - *paymentId*    : The payment order ID generated by Antom, used for refunds and reconciliation.
> - *paymentAmount*    : The payment amount.

**Tab: Inquire about the result**

You can also inquire about the payment or authorization result by calling the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API using     *paymentRequestId*     from the payment request,    regardless of whether it is an APM payment, card payment, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), or [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md).

 The following sample code shows how to call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API:

 ```java
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
    }
}
```

 The following sample code shows a request message:

 ```json
{
  "paymentRequestId": "paymentRequestId01"
}
```

 The following sample code shows a response message:

 ```json
{
  "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"
  }
}
```

 Please handle the result based on the value of the     *paymentStatus*     parameter in the response. For specific return values, refer to the   [API documentation](https://docs.antom.com/ac/ams/paymentri_online.md#Responseparameters-paymentStatus)  .

 > **[INFO]** **Common questions**
>
>  **Q: What key parameters should I pay attention to when using the**       [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)       **API to check the payment or authorization status?**
>
>  A: Please note the following key parameters:
>
>  - *result*    :    Only indicates the result of the API call. For APM payments, the final payment result should be determined based on     *paymentStatus*     (  `SUCCESS`  /  `FAIL`  /  `PROCESSING`  ). For card payments, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), and [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md),     *paymentStatus*     only represents the authorization result, and the decision to ship goods should rely on the capture result.
> - *paymentAmount*    : Used to verify the payment amount.
> - *paymentId*    : The payment order ID generated by Antom, used for refunds and reconciliation.
>
>  ​
>
>  **Q: How often should I call the inquiryPayment API?**
>
>  A: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API constantly with an interval of 2 seconds until the final payment result is obtained or an asynchronous payment result notification is received.

<!-- /TabGroup -->

## (Optional) Step 4: Capture   **[Server-side]** {#qu0V5}

 > **[INFO]** **Note**    : Card payments and certain APM payment methods (such as [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Pay by Bank](https://docs.antom.com/ac/antomop/pay_by_bank.md)) must undergo a capture process, and capture will only be triggered after a successful authorization.

 After successful authorization, Antom will automatically initiate capture by default, but also supports you to initiate manual capture. Antom will send the capture result notification to you via the   [**notifyCapture (One-time Payments)**](https://docs.antom.com/ac/ams/notify_capture.md)   API, while you can also query the capture result. You need to decide whether to ship goods based on the capture result. For specific operations, refer to   [Capture](https://docs.antom.com/ac/cashierpay/capture.md)  .

## Step 5: Obtain subscription notifications   **[Server-side]** {#AQnvC}

**Tab: iOS**

# User experience {#B2EWwv}

 The following figures illustrates the user experience of integrating Payment Element for the first-time subscription and subsequent deductions:

## First-time subscription {#o2Fdja}

<!-- TabGroup -->

**Tab: APM payments**

The figure below illustrates the buyer's user experience for APM payments:

 ![APM first-time subscription payment flow using Antom Payment Element for iOS terminal](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/bb97c854-32e1-4cc8-a18e-daeb89040b01.png)

**Tab: Card payments**

The figure below illustrates the buyer's user experience for card payments:

 ![Card first-time subscription payment flow using Antom Payment Element for iOS terminal](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/9205de62-ad6a-4831-a920-e49ca4711826.png)

<!-- /TabGroup -->

## Subsequent deductions {#F22h4C}

 Antom server will automatically initiate subsequent periodic deductions. The merchant server will receive subscription renewal payment notifications to renew the subscription service for the buyer. This process involves no page interaction.

# Order lifecycle {#i2YCQo}

 Learn about the lifecycle of different payment methods:

<!-- TabGroup -->

**Tab: APM Payments**

The following figure illustrates the subscription lifecycle for APM payments, including creating a subscription, signing and binding a payment method, completing the initial deduction, and initiating refunds when necessary. This process ensures the normal activation of subscriptions and secure, transparent handling of fees:

 ![Subscription lifecycle diagram for APM payments](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/428e0e06-2bad-4df7-bb44-e0c6735ff961.png)

**Tab: Card payments, Apple Pay, Google Pay**

The following figure illustrates the subscription lifecycle for card payments, [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), and [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), including creating a subscription, signing and binding a payment method, completing the initial deduction, and initiating refunds when necessary. This process ensures the normal activation of subscriptions and secure, transparent handling of fees:

 ![Subscription lifecycle diagram for card payments, Google Pay, and Apple Pay](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/c83d8e1a-edd1-4583-b156-49012af04486.png)

<!-- /TabGroup -->

 ![Subscription status and notification flow during the subscription lifecycle for Payment Element integration](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/360391dc-b3bd-41d5-80eb-de906fbe694c.png)

 ![iOS subscription state transition during the Payment Element subscription lifecycle](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/656c38b2-6ea4-4943-ae15-ae980a6cf7d9.png)

# Payment flow {#ni2JF}

 The following flow illustrates how to integrate Subscription Payment using Payment Element:

<!-- TabGroup -->

**Tab: APM Payments**

<!-- TabGroup -->

**Tab: First-time subscription**

![APM first-time subscription payment flow using Payment Element for iOS terminal](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/e70bd180-e7cf-4993-88f9-2af39f650cfb.png)

**Tab: Subsequent deductions**

![APM subsequent recurring deductions using Payment Element](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/21a235fb-e31a-467b-8a6e-0db536e6767e.png)

<!-- /TabGroup -->

**Tab: Card payments, Apple Pay, Google Pay**

<!-- TabGroup -->

**Tab: First-time subscription**

![Card, Apple Pay, and Google Pay first-time subscription payment flow using Payment Element for iOS terminal](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/233677b8-1cf8-4ae3-85e6-d4d77d784e1b.png)

**Tab: Subsequent deductions**

![Card, Apple Pay, and Google Pay subsequent recurring deductions using Payment Element](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/5926a000-fb1c-4fcf-b194-2397b614ee61.png)

<!-- /TabGroup -->

<!-- /TabGroup -->

<!-- TabGroup -->

**Tab: First-time subscription**

1. **The buyer enters the subscription product page and initiates payment.**  
   Merchant client collects relevant information about the buyer's subscription.
2. **Create a payment session request.**  
 Call the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)        API to obtain the payment session. You can specify one or multiple payment method types, or leave the payment method type unspecified, to submit the payment request.
3. **Invoke Payment Element.**  
   On the client side, invoke Payment Element using the payment session. You can choose to use the Payment Element-rendered payment method list, or render the payment methods by specifying them yourself. Payment Element will handle information processing, collect payment details, perform redirects, manage app invocations, display QR codes, and conduct validations based on the features of the selected payment method. After the payment is completed, depending on your configuration and the payment method features, you need to handle redirections based on the result returned by the   `onSubmitPayCallback:`   method, or the system will automatically return to your result page.
4. **Obtain the authorization or payment result.**  
   You can obtain the payment or authorization results through one of the following two methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)      to set the address for receiving asynchronous notifications. When the payment is successful or expires, Antom will use   [**notifyPayment**](https://docs.antom.com/ac/ams/paymentrn_online.md)   to send asynchronous notifications to you.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the payment or authorization status.

 > **[INFO]** **Note**    : For card payments and certain APM payment methods (such as [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Pay by Bank](https://docs.antom.com/ac/antomop/pay_by_bank.md)), these payment methods follow an authorized-capture mode. The steps above only complete the authorization phase, indicates that the buyer has completed the payment using their card, and the funds are in a frozen state. To transfer the buyer's frozen funds to your account, you must integrate the capture step. The successful capture result will serve as the basis for shipping goods.

 5. **(Optional) Capture and obtain the capture result.**

 By default, Antom automatically handles fund capture on your behalf. You can also manually capture funds by calling the   [**capture (One-time Payments)**](https://docs.antom.com/ac/ams/capture.md)   API. The capture result can be obtained through one of the following methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)   to set the address for receiving asynchronous notifications. Upon capture completion, Antom will send you asynchronous notifications via the   [**notifyCapture (One-time Payments)**](https://docs.antom.com/ac/ams/notify_capture.md)   API.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the capture status.

 6. **Obtain subscription notifications.**  
   Once the subscription is activated, Antom will send initial subscription notifications and subscription renewal notifications to you.

**Tab: Subsequent deductions**

1. **Antom server initiates a deduction from the payment method.**
2. **Obtain the authorization or payment result.**  
   You can obtain the payment or authorization results through one of the following two methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)      to set the address for receiving asynchronous notifications. When the payment is successful or expires, Antom will use   [**notifyPayment**](https://docs.antom.com/ac/ams/paymentrn_online.md)   to send asynchronous notifications to you.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the payment or authorization status.

 > **[INFO]** **Note**    : For card payments and certain APM payment methods (such as [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Pay by Bank](https://docs.antom.com/ac/antomop/pay_by_bank.md)), these payment methods follow an authorized-capture mode. The steps above only complete the authorization phase, indicates that the buyer has completed the payment using their card, and the funds are in a frozen state. To transfer the buyer's frozen funds to your account, you must integrate the capture step. The successful capture result will serve as the basis for shipping goods.

 3. **(Optional) Capture and obtain the capture result.**

 For subsequent deductions, Antom automatically handles fund capture on your behalf. You can obtain the capture result through one of the following methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)   to set the address for receiving asynchronous notifications. Upon capture completion, Antom will send you asynchronous notifications via the   [**notifyCapture (One-time Payments)**](https://docs.antom.com/ac/ams/notify_capture.md)   API.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the capture status.

 4. **Obtain subscription notifications.**  
   After the deduction is successful or fails, Antom will send you a subscription deduction notification.

<!-- /TabGroup -->

# Integration preparations {#22KAW}

 Before you start integrating, read   [Integration guide](https://docs.antom.com/integration_guide_en.md)   and   [API overview](https://docs.antom.com/ac/ams/api_fund.md)   to understand the integration steps of the server-side API and the precautions for calling the API. Furthermore, ensure the following prerequisites are met:

 - Obtained your client ID
- Complete the key configuration
- Complete the configuration of     *paymentNotifyUrl*     to receive the asynchronous notification
- Integrate the server-side SDK package, install the server-side library, and initialize a request instance. For more details, refer to   [Server-side SDKs](https://docs.antom.com/ac/sdks/server_sdks.md)  .
- Integrate the client-side SDK package by following the steps detailed in   [Integrate the SDK package for iOS](https://docs.antom.com/ac/sdks/ios.md)  , and ensure to use the latest SDK version or no lower than 1.46.0.

# Integration steps {#n2t0Y}

 Start your integration by taking the following steps:

 1. (Optional) Preload the SDK
2. Create a payment session
3. Invoke Payment Element
4. Obtain the authorization or payment result
5. (Optional) Capture
6. Obtain subscription notifications

## (Optional) Step 1: Preload the SDK   **[Client-side]** {#c2hKYz}

 Before creating a payment session, it is highly recommended that you preload the SDK to improve the rendering speed of the checkout page, reducing the waiting time for buyers during the payment. Follow the code example below to perform the preloading:

 ```objectivec
[AMSPaymentElement.shared preload];
```

## Step 2: Create a payment session   **[Server-side]** {#K2uWal}

 Call the      [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API with order information to create a payment session and obtain the     *paymentSessionData*     required to invoke Payment Element. You can choose to either render the payment methods by specifying them yourself or use the Payment Element-rendered payment method list. It is recommended that you create a payment session after the buyer clicks the payment button. Depending on the method you selected for rendering the payment method list at the checkout page, pass the corresponding parameters when calling the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API.

 - Render the payment methods by yourself:    When rendering the payment method list yourself, you must pass the parameters for specifying payment methods listed in the table below. If Payment Element needs to collect payment details, pass the card payment parameters listed in the table below.

 - When using the Payment    Element-rendered    payment method list, you only need to pass the basic parameters listed in the table below. Payment Element renders all supported payment methods on the checkout page by default, but you can [specify payment methods](#L9xOX) to display only the options you need.

 | **Type** | **Parameter name** | **Required** | **Description** |
| --- | --- | --- | --- |
| Basic parameters ​ | *productCode* | Yes | For    Payment    Element   , the value is fixed as      `CASHIER_PAYMENT`  . |
| *productScene* | Yes | For    Payment    Element   , the value is fixed as      `ELEMENT_PAYMENT`  . |  |
| *paymentRequestId* | Yes | The unique ID assigned by the merchant to identify a payment request. |  |
| *paymentAmount* | Yes | The payment amount that the merchant requests to receive in the order currency. |  |
| *paymentRedirectUrl* | Yes | ​   The merchant page URL that the buyer is redirected to after the payment is completed. |  |
| *paymentNotifyUrl* | No | ​   The payment result notification URL, which can be passed in through the API or set as a fixed value through Antom Dashboard. If both are set, the value in the API takes precedence. |  |
| *settlementStrategy* | Yes | The settlement strategy for the payment request. |  |
| *order.referenceOrderId* | Yes | The unique ID to identify the order on the merchant side. |  |
| *order.orderDescription* | Yes | Summary description of the order. |  |
| *env.terminalType* | No | Terminal type of which the merchant service applies to. Valid values are: - `WEB`  : The client-side terminal type is a website, which is opened via a PC browser. - `WAP`  : The client-side terminal type is an H5 page, which is opened via a mobile browser. - `APP`  : The client-side terminal type is a mobile application.   > **[INFO]** **Note**    : This parameter is required for [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md). |  |
| *env.osType* | No | The operating system type from which the buyer initiated the transaction. Valid values: - `IOS`  : Apple's iOS. - `ANDROID`  : Google's Android.   > **[INFO]** **Note**    : Specify this parameter when     *env.TerminalType*     is   `WAP`   or   `APP`  . |  |
| *env.clientIp* | No | The IP address of the client device. > **[INFO]** **Note**    : This parameter is required for card payments, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md). |  |
| *subscriptionInfo.subscriptionDescription* | Yes | The description of the subscription. |  |
| *subscriptionInfo.subscriptionStartTime* | Yes | The date and time when the subscription becomes active. |  |
| *subscriptionInfo.subscriptionNotifyUrl* | Yes | The URL that is used to receive the subscription result notification. HTTPS address must be provided. |  |
| *subscriptionInfo.subscriptionExpiryTime* | No | The specific date and time when the created subscription expires. After the subscription expires, the order must be terminated. Default values for this parameter: - Card payments, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md): 7 days (calculated from the time the subscription creation request is sent). - APM payments: 80 minutes (calculated from the time the subscription creation request is sent).   > **[INFO]** **Note**    : For APM payment methods, you need to set the subscription expiration time to greater than 74 minutes. |  |
| *subscriptionInfo.periodRule* | Yes | The subscription period type, supporting four types of cycles:   `YEAR`  ,   `MONTH`  ,   `WEEK`  ,   `DAY`  . |  |
| Card payment parameters (Refer to   [Card payment features](#abqU1)  ) | *paymentFactor.captureMode* | No | Indicates the method for capturing funds after the user authorizes the payment. Valid values are: - `AUTOMATIC`  : indicates that Antom automatically captures the funds after the authorization. The same applies when the value is empty or you do not pass in this parameter. - `MANUAL`  : indicates that you manually capture the funds by calling the   [**capture (Checkout Payment)**](https://docs.antom.com/ac/ams/capture.md)   API. |
| *order.buyer* | Yes ​ | Buyer information. At least one of the following must be provided: - *order.buyer.referenceBuyerId* - *order.buyer.buyerPhoneNo* - *order.buyer.buyerEmail* |  |
| *availablePaymentMethod.paymentMethodMetaData.is3DSAuthentication* | No | Determine whether to set the transaction authentication type to 3DS based on risk and dispute history. Valid values are: - `true`  :    indicates that the transaction authentication type is    3DS authentication. - `false`  :    indicates that the transaction authentication type is    Non-3DS authentication. Default if empty or not provided. |  |
| *availablePaymentMethod.paymentMethodMetaData.tokenizeMode* | No | The tokenize mode. Indicates whether card information needs to be stored. When you opt to   [store card details](https://docs.antom.com/ac/pm/tokenization.md)  , the actual information is replaced with a unique and secure token that can be used for future payments. Valid values are: - `ENABLED`  : indicates that you obtain card binding authorization and want to store card information for future payments. - `DISABLED`  : i   ndicates that you don’t need to store card information. The same applies when the value is empty or you do not specify this parameter. - `ASKFORCONSENT`  : indicates that you present a button on the page, allowing the buyer to choose whether or not they want to store the card information. |  |
| *availablePaymentMethod.paymentMethodMetaData.isCardOnFile* | No | Indicates whether the card used in this transaction has been   [previously processed and stored](https://docs.antom.com/ac/pm/cof.md)   by the merchant. Valid values are: - `true`  : The card used in the transaction has been previously processed and stored by the merchant. Set to   `true`   for stored card payments. - `false`  : Default. The card is new and has not been used previously on the merchant side. |  |
| *savedPaymentMethods.paymentMethodId* | No | Used for stored card payments.    The value of this parameter is that of     *cardToken*     obtained from the new card payment. |  |
| Parameters for specifying payment methods (Refer to   [Specify payment methods](#L9xOX)  ) | *availablePaymentMethod.paymentMethodTypeList.paymentMethodType* | No | Used to specify payment methods. You can specify one or multiple payment methods. |
| *availablePaymentMethod.paymentMethodTypeList.paymentMethodOrder* | No | Set the priority order of payment methods. |  |
| *availablePaymentMethod.paymentMethodTypeList.expressCheckout* | ​ | Specifies whether to enable the express checkout option for a payment method. Currently, only   `ALIPAY_CN`  ,   `APPLEPAY`   and   `GOOGLEPAY`   can be set as express checkout methods. Valid values are: - `true`  : Enables express checkout for this payment method. - `false`  : Disables express checkout for this payment method.   > **[INFO]** **Note**    : This parameter is currently supported only on Android and iOS. |  |

 The above parameters are the basic parameters for creating a payment session, for full parameters and additional requirements for certain payment methods refer to   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   **.**

 The following sample code shows how to call the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)        API:

 ```java
public ResponseEntity<ApiResponse> createPaymentSession(@RequestBody PaymentVO payment) {
    AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
    alipayPaymentSessionRequest.setProductCode(ProductCodeType.CASHIER_PAYMENT);
    alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.ELEMENT_PAYMENT);

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

    // convert amount unit(in practice, amount should be calculated on your serverside)
    // For details, please refer to: <a href="https://docs.antom.com/ac/ref/cc">Usage rules of the Amount object</a>
    long amountMinorLong = Money.of(CurrencyUnit.of(payment.currency), new BigDecimal(payment.amountValue)).getAmountMinorLong();

    // set amount
    Amount amount = Amount.builder().currency(payment.currency).value(String.valueOf(amountMinorLong)).build();
    alipayPaymentSessionRequest.setPaymentAmount(amount);

    // set settlement strategy
    // replace with your existing settlement currency
    SettlementStrategy settlementStrategy = SettlementStrategy.builder().settlementCurrency("SGD").build();
    alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

    // 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 sdk testing order").orderAmount(amount).buyer(buyer).build();
    alipayPaymentSessionRequest.setOrder(order);

    // replace with your notify url
    // or configure your notify url here: <a href="https://dashboard.antom.com/global-payments/developers/iNotify">Notification URL</a>
    alipayPaymentSessionRequest.setPaymentNotifyUrl("http://www.yourNotifyUrl.com/payment/receivePaymentNotify");

    // replace with your redirect url
    alipayPaymentSessionRequest.setPaymentRedirectUrl(
        "http://localhost:8080/index.html?paymentRequestId=" + paymentRequestId);

    // set subscription info
    PeriodRule periodRule = PeriodRule.builder().periodCount(1).
        periodType("MONTH").build();

    List trials = new ArrayList<Trial>();

    Trial trial = Trial.builder().
        trialAmount(amount).
        trialStartPeriod(1).
        trialEndPeriod(2).
        build();

    trials.add(trial);

    SubscriptionInfo subscriptionInfo = SubscriptionInfo.builder().
        subscriptionDescription("Subscription description").
        subscriptionStartTime("2026-03-11T09:48:17+08:00").
        subscriptionEndTime("2026-11-21T09:48:17+08:00").
        periodRule(periodRule).
        trials(trials).
        subscriptionNotifyUrl("https://your.example.com/subscriptionNotify").
        subscriptionExpiryTime("2026-03-12T09:48:17+08:00").
        build();
    alipayPaymentSessionRequest.setSubscriptionInfo(subscriptionInfo);

    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));
}
```

<!-- TabGroup -->

**Tab: Merchant-rendered payment method list**

If you render the payment method list yourself, you must integrate by specifying individual payment methods. The following code shows a sample of the request message:

 ```json
{
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "USD",
      "value": "2900"
    },
    "orderDescription": "antom sdk testing order",
    "referenceOrderId": "4b085ec4-9999-4296-8f00-479e929edb2c"
  },
  "paymentAmount": {
    "currency": "USD",
    "value": "2900"
  },
  "paymentNotifyUrl": "http://www.yourNotifyUrl.com/payment/receivePaymentNotify",
  "paymentRedirectUrl": "http://google.com.my",
  "paymentRequestId": "PAYMENT_20260313102551842_AUTO",
  "productCode": "CASHIER_PAYMENT",
  "productScene": "ELEMENT_PAYMENT",
  "settlementStrategy": {
    "settlementCurrency": "USD"
  },

  "availablePaymentMethod": {
    "paymentMethodTypeList": [

      {
        "paymentMethodType": "ALIPAY_CN" // Specify payment method
      },

      {
        "paymentMethodType": "TNG"
      }

    ]
  },

  "subscriptionInfo": {
    "periodRule": {
      "periodCount": 1,
      "periodType": "MONTH"
    },
    "subscriptionDescription": "Subscription description",
    "subscriptionNotifyUrl": "https://your.example.com/subscriptionNotify",
    "subscriptionStartTime": "2026-03-11T09:48:17+08:00"

  }
}
```

**Tab: Payment Element-rendered payment method list**

If you use Payment Element-rendered payment method list, Payment Element will display all supported payment methods by default. The following code shows a sample of the request message:

 ```json
{
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "SGD",
      "value": "2900"
    },
    "orderDescription": "antom sdk testing order",
    "referenceOrderId": "4b085ec4-9999-4296-8f00-479e929edb2c"
  },
  "paymentAmount": {
    "currency": "SGD",
    "value": "2900"
  },
  "paymentNotifyUrl": "http://www.yourNotifyUrl.com/payment/receivePaymentNotify",
  "paymentRedirectUrl": "http://localhost:8080/index.html?paymentRequestId=7a6252b5-6d58-4033-882e-2eff7f262b35",
  "paymentRequestId": "7a6252b5-6d58-4033-882e-2eff7f262b35",
  "productCode": "CASHIER_PAYMENT",
  "productScene": "ELEMENT_PAYMENT",
  "settlementStrategy": {
    "settlementCurrency": "SGD"
  },
  "subscriptionInfo": {
    "periodRule": {
      "periodCount": 1,
      "periodType": "MONTH"
    },
    "subscriptionDescription": "Subscription description",
    "subscriptionEndTime": "2026-11-21T09:48:17+08:00",
    "subscriptionExpiryTime": "2026-03-12T09:48:17+08:00",
    "subscriptionNotifyUrl": "https://your.example.com/subscriptionNotify",
    "subscriptionStartTime": "2026-03-11T09:48:17+08:00",
    "trials": [
      {
        "trialAmount": {
          "$ref": "$.order.orderAmount"
        },
        "trialEndPeriod": 2,
        "trialStartPeriod": 1
      }
    ]
  }
}
```

<!-- /TabGroup -->
 The following code shows a sample of the response, which contains the following parameters:

 - *result.resultStatus*    :    The result of the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API call.
- *paymentSessionData*    : The payment session data to be returned to the client.
- *paymentSessionExpiryTime*    : The expiration time of the payment session.

 ```json
{
  "paymentSessionData": "WNoudiUNUDjHjx0oXddzWn0QrRHv52lrHFNMKc8/5uKSqNDklmQ7Nh4JGBNITqi3jgIASZkbpq15gIEQleY13A==&&SG&&188&&eyJhY3Rpb24iOnsibmVlZFZlcmlmeUFuZFJlc3VtZSI6ZmFsc2UsInNpZ25CdXR0b25EaXNwbGF5IjpmYWxzZSwic2tpcFNka1F1ZXJ5IjpmYWxzZSwidXNlclNpZ25BZ3JlZW1lbnQiOmZhbHNlfSwiY2xpZW50SWQiOiJTQU5EQk9YXzVZRVYxRzJaVjQzSzA3MjE3IiwiY29ubmVjdEZhY3RvciI6eyJlbmFibGVDb25uZWN0IjpmYWxzZX0sImVsaWdpYmxlRWFzeVBheU1hcmtldGluZyI6ZmFsc2UsImV4dGVuZEluZm8iOiJ7XCJPUEVOX01VTFRJX1BBWU1FTlRfQUJJTElUWVwiOlwidHJ1ZVwiLFwiZXhwcmVzc0NoZWNrb3V0XCI6XCJmYWxzZVwiLFwidmVyc2lvbk1hcFwiOlwie1xcXCJ3ZWJcXFwiOntcXFwiMS4xLjBcXFwiOntcXFwidGFyZ2V0V2ViVmVyaXNvblxcXCI6XFxcIjEuMS4wXFxcIn0sXFxcIjEuMi4wXFxcIjp7XFxcInRhcmdldFdlYlZlcmlzb25cXFwiOlxcXCIxLjIuMFxcXCJ9fSxcXFwiaU9TXFxcIjp7XFxcIjEuMS4wXFxcIjp7XFxcInRhcmdldFdlYlZlcmlzb25cXFwiOlxcXCIxLjEuMFxcXCJ9LFxcXCIxLjIuMFxcXCI6e1xcXCJ0YXJnZXRXZWJWZXJpc29uXFxcIjpcXFwiMS4yLjBcXFwifX0sXFxcIkFuZHJvaWRcXFwiOntcXFwiMS4xLjBcXFwiOntcXFwidGFyZ2V0V2ViVmVyaXNvblxcXCI6XFxcIjEuMS4wXFxcIn0sXFxcIjEuMi4wXFxcIjp7XFxcInRhcmdldFdlYlZlcmlzb25cXFwiOlxcXCIxLjIuMFxcXCJ9fX1cIn0iLCJuZWVkQWNjb3VudENvbmZpcm1QYWdlIjpmYWxzZSwicGF5bWVudFNlc3Npb25Db25maWciOnsicGF5bWVudE1ldGhvZENhdGVnb3J5VHlwZSI6IkFMTCIsInByb2R1Y3RTY2VuZSI6IkVMRU1FTlRfUEFZTUVOVCIsInByb2R1Y3RTY2VuZVZlcnNpb24iOiIxLjAifSwic2tpcFJlbmRlclBheW1lbnRNZXRob2QiOmZhbHNlfQ==",
  "paymentSessionExpiryTime": "2026-03-12T15:24:24+08:00",
  "paymentSessionId": "WNoudiUNUDjHjx0oXddzWn0QrRHv52lrHFNMKc8/5uIsshVVNylGbBzLF2n6JmoT",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
```

 The table below shows the possible values of     *result.resultStatus*     in the response. Please handle the result according to the guidance provided:

   | ***result.resultStatus*** | **Message** | **Further action** |
| --- | --- | --- |
| `S` | The payment session was successfully created, and     *paymentSessionData*     is returned. | Proceed to the next steps. |
| `U` | Unknown error. | Please use a new          *paymentRequestId*     and call the API again. If the issue persists, contact Antom Technical Support. |
| `F` | Failed to create the payment session. | Please check and verify whether the current API required request fields (including header fields and body fields) are correctly passed and valid. |

 > **[INFO]** **Note**    : If no response is received, it may indicate a network timeout. Please use a new     *paymentRequestId*     and call the API again. If the issue persists, contact Antom Technical Support.

 > **[INFO]** **Common questions**
>
>  **Q: Can I use Chinese characters in the value of the request parameters?**
>
>  A: To avoid incompatibility of certain payment methods, do not use Chinese characters for fields in the request.
>
>  ​
>
>  **Q: How to set the address to receive payment notification?**
>
>  A: Specify     *paymentNotifyUrl*     in the      [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API to receive the asynchronous notification about the payment result (  [**notifyPayment**](https://docs.antom.com/docs/ac/ams/paymentrn_online.md)  ), or configure the receiving URL in Antom Dashboard. If the URL is specified in both the request and Antom Dashboard, the value specified in the request takes precedence.
>
>  ​
>
>  **Q: Does the returned**       ***paymentSessionData***       **require processing before passing it to the client?**
>
>  A: Do not process or modify     *paymentSessionData*     in any way, as this may cause the Payment Element invocation to fail.
>
>  
>  **Q: Is 3DS authentication mandatory for the first transaction?**
>
>  A: The first transaction requires buyer participation and identity verification to ensure the security of subsequent recurring payments (where the buyer is not present). Specific requirements are as follows:
>
>  - Buyer uses card payment: Whether you or Antom collects card information, 3DS authentication must be completed. You can set     *is3DSAuthentication*     to   `true`   in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   request to enable Antom 3DS authentication.
> - Buyer uses [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md) and Antom decrypts the data: You need to set     *is3DSAuthentication*     to   `true`   to initiate a 3DS transaction for identity verification. If the decrypted data is a DPAN (Device Primary Account Number), the system will automatically proceed with the authorization process without requiring additional buyer verification.
> - Buyer uses [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and Antom decrypts the data: You can set     *is3DSAuthentication*     to   `false`   or omit this parameter, as the buyer has already completed Apple's verification process.

## Step 3: Invoke Payment Element   **[Client-side]** {#F2WfpW}

 Use     *paymentSessionData*     to invoke Payment Element on your client. After the buyer clicks to submit payment, Payment Element will handle the entire flow based on the selected payment method, including displaying QR codes, redirecting to payment pages, performing 3DS authentication, and returning to the merchant’s result page.

 1. After obtaining     *paymentSessionData*     from the server, use the   `AMSPaymentElement`   class to create a Payment Element instance.

   1. Creating the   `AMSPaymentElementConfiguration`   object includes the following parameters:

 | **Parameter name** | **Required** | **Type** | **Description** |
| --- | --- | --- | --- |
| *locale* | No | String | Used to pass in language information. Pass the corresponding value based on the region of the payment method. Valid values are listed as follows. If other values are passed, English is used by default: - `en_US`  : Default value.    English - `pt_BR`  : Portuguese (Brazil) - `pt_PT`  : Portuguese - `es_ES`  : Spanish - `ko_KR`  : Korean - `zh_CN`  : Simplified Chinese - `zh_HK`  : Traditional Chinese - `ms_MY`  : Malay - `in_ID`  : Indonesian - `th_TH`  : Thai - `vi_VN`  : Vietnamese - `tl_PH`  : Filipino - `it_IT`  : Italian - `de_DE`  : German - `fr_FR`  : French - `nl_NL`  : Dutch - `ja_JP`  : Japanese - `ro`  : Romanian - `pl_PL`  : Polish - `ar_SA`  : Arabic - `tr_TR`  : Turkish - `hi_IN`  : Hindi |
| *options* | No | Dictionary | Used to specify whether to use the default loading mode and sandbox environment. Valid values are: - `"sandbox", "true"`  ：Sandbox environment. - `"sandbox", "false"`  ：Default value. Production environment. - `"showLoading", "true"`  ：   Default value   . The default loading mode is used. - `"showLoading", "false"`  ：The default loading mode is not used.. - `"notRedirectAfterComplete"`  ：Optional. Boolean type. Used to set whether to redirect back to your page after payment completion. Valid values are:     - `false`  ：Default value. Redirects to your page after successful payment completion. The same applies if the value is empty.   - `true`  ：No redirection after payment completion. You need to manually control the subsequent payment flow based on client-side event codes.   > **[INFO]** **Note**    : The payment result event codes returned on the client-side are for reference only in page navigation. Transaction status updates must be based on the results returned by the server-side   [**notifyPayment**](https://docs.antom.com/ac/ams/paymentrn_online.md)   or   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API.   - `"appearance"`  :    Optional.    Used for custom appearance configuration. For more information, refer to   [Appearance customization](https://docs.antom.com/ac/cashierpay/appearance.md)  . It contains the following parameters:     - *theme*    : Theme color.   - *layout*    : Arrangement method.   - *variables*    : Override underlying CSS custom properties using CSS design tokens (e.g.,   `color-primary`  ) to implement theme customization. |

   2. Implement   `AMSPaymentProtocol`   to handle corresponding events in subsequent processes. It includes the following methods:

   | **Method** | **Required** | **Description** |
| --- | --- | --- |
| `initConfiguration:completion:` | Yes | A callback function for monitoring exceptions during payment component initialization. The callback parameters include the error     *code*    . Refer to the   [callback function event codes](https://docs.antom.com/ac/sdks/ref.md#l1qwb)   for handling. |
| `onSubmitPayCallback:` | No | A callback function for monitoring exceptions when launching the payment page. The callback parameters include the error     *code*     and     *message*    . Refer to the   [callback function event codes](https://docs.antom.com/ac/sdks/ref.md#UOmXC)   for handling. |

 The following shows a sample code for creating the Payment Element instance using   `AMSPaymentElement`  :

   ```objectivec
#import <AMSComponent/AMSComponent-Swift.h>

// Create an AMSPaymentElementConfiguration object
AMSPaymentElementConfiguration *componentConfig = [AMSPaymentElementConfiguration new];
componentConfig.locale = @"en_US";

NSString *appearance = @"{\n  \"theme\": \"night\",\n  \"layout\": {\n    \"type\": \"Tabs\"\n  },\n  \"variables\": {\n    \"content-quaternary\": \"#FFFF00\"\n  }\n}";

// Set sandbox environment. If left empty, the online production environment will be used by default
NSDictionary *options = @{@"showLoading": @"true",
                          @"sandbox": @"true",
                          @"appearance": appearance
                          };
componentConfig.options = options;

// initConfiguration usage
[[AMSPaymentElement shared] initConfiguration:componentConfig completion:^(AMSStatusResult * _Nonnull result) {
    // Handle error events during the initConfiguration phase
    if (result.error) {
        // Handle failure
        if ([result.error.code isEqualToString:@"UI_STATE_ERROR"]) {
            NSLog(@"integration code error, please check the integration code");
        } else {
            NSLog(@"unknown error, please contact support");
        }
    } else {
        // Handle success
    }
}];

// Set callback to listen for payment events on the checkout page
[AMSPaymentElement shared].paymentDelegate = self;

// Server calls the Create Payment Session API to obtain paymentSessionData

#pragma AMSPaymentProtocol
// Handle submitPay phase event codes via callback
- (void)onSubmitPayCallback:(AMSStatusResult *)eventResult {
    AMSStatusResultType statusType = eventResult.status;
    AMSResultError *error = eventResult.error;

    switch (statusType) {
        case AMSStatusResultTypePROCESSING:
            if (error && error.code) {
                if ([@"PAYMENT_IN_PROCESS" isEqualToString:error.code]) {
                    NSLog(@"payment is in processing, please try polling the payment result from the server");
                } else if ([@"USER_CANCELED" isEqualToString:error.code]) {
                    NSLog(@"user cancelled the payment process, please try invoke createComponent again");
                } else if ([@"UNKNOWN_EXCEPTION" isEqualToString:error.code]) {
                    NSLog(@"unknown exception, please contact support");
                } else if ([@"PAYMENT_RESULT_TIMEOUT" isEqualToString:error.code]) {
                    NSLog(@"get payment result timeout, please try polling the payment result from the server");
                }
            }
            break;
        case AMSStatusResultTypeCANCELLED:
            // fall through
        case AMSStatusResultTypeSUCCESS:
            NSLog(@"payment cancelled or success, do nothing");
            break;
        case AMSStatusResultTypeFAIL:
            if (error && error.code) {
                if ([@"ORDER_IS_CANCELLED" isEqualToString:error.code]) {
                    NSLog(@"the merchant has proactively canceled the order, please check on your own.");
                } else if ([@"ORDER_IS_CLOSED" isEqualToString:error.code]) {
                    NSLog(@"the order has timed out and is closed, please re-initiate payment using a new paymentRequestId.");
                } else {
                    NSLog(@"unknown error, please contact support");
                }
            }
            break;
        default:
            break;
    }
}
```

   2. Use the   `createComponent`   function from the instance object to invoke Payment Element:

   | **Method** | **Required** | **Description** |
| --- | --- | --- |
| `createComponent:completion:` | No | A callback function for monitoring payment events on the checkout page. The callback parameters include the error     *code*    . Refer to the   [callback function event codes](https://docs.antom.com/ac/sdks/ref.md#efco9k)   for handling. |

   ```objectivec
// Callback event code method - createComponent usage
[[AMSPaymentElement shared] createComponent:paymentSessionData completion:^(AMSStatusResult * _Nonnull result) {
    if (result.error) {
        // Handle failure
        if ([result.error.code isEqualToString:@"UI_STATE_ERROR"]) {
            NSLog(@"integration code error, please check the integration code");
        } else if ([result.error.code isEqualToString:@"PARAM_INVALID"]) {
            NSLog(@"session data invalid, please check the session data");
        } else if ([result.error.code isEqualToString:@"INITIALIZE_WEB_TIMEOUT"]) {
            NSLog(@"web app timeout, please invoke component again");
        } else {
            NSLog(@"unknown error, please contact support");
        }
    } else {
        // Handle success
    }
}];

```

### Unmount Payment Element {#f31st}

 Call the   `onDestroy`   method to release SDK component resources in the following scenarios:

 - When the buyer navigates away from the checkout page, release the component resources created by the    ​  [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API   .
- When the buyer initiates multiple payments and the parameters in `initConfiguration` had changed, release the component resources previously created by the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API.

   ```objectivec
// Release SDK component resources
[[AMSPaymentElement shared] onDestroy];
```

   You do not need to call   `onDestroy`   in the following scenarios, the SDK automatically releases resources:

 - When the buyer initiates multiple payments and the parameters in   `AMSPaymentElementConfiguration`   remain unchanged. The SDK will automatically reclaim some resources after payment concludes to reset to the state before   `createComponent`  .

### Redirect to the merchant page {#zyDEQ}

 The following scenarios describe how redirecting to the merchant page and returning payment results are handled. Please follow the guidelines below:

 | **"notRedirectAfterComplete"** | **Payment result** | **Recommended action** |
| --- | --- | --- |
| `true` | Success/Failure | If you set     *notRedirectAfterComplete*     to   `true`   when creating the Payment Element instance and the payment method supports completing payments within the SDK, the payment result will be returned via the   `onSubmitPayCallback:`   method. You need to handle the redirect logic yourself based on the   [event code](https://docs.antom.com/ac/sdks/ref.md#UOmXC)   returned by   `onSubmitPayCallback:`  . |
| `false` | Success | After payment is completed, Payment Element will automatically redirect to the     *paymentRedirectUrl*     result page you provided in the      [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API. |
| `false` | Failure | The payment result will be returned via the   `onSubmitPayCallback:`   method. You need to handle the redirect logic yourself based on the   [event code](https://docs.antom.com/ac/sdks/ref.md#UOmXC)   returned by   `onSubmitPayCallback:`  . |

 > **[INFO]** **Note**    :
>
>  - If the payment requires redirecting to an external page (the payment method does not support payment within the SDK), the payment result will not be returned via   `onSubmitPayCallback:`  . After completing the payment on the external payment page, the payment method will determine whether to automatically redirect back to the     *paymentRedirectUrl*     you provided.
> - The payment results returned by the   `onSubmitPayCallback:`   method are only for client-side page navigation and status display. For the final order status, please obtain it through   [Step 4: Obtain the authorization or payment result](#2MxqX3)  .

 > **[INFO]** **Common questions**
>
>  **Q: What should be noted when passing the**       ***paymentRedirectUrl***       **parameter?**
>
>  A:     *paymentRedirectUrl*     must be set to an HTTPS address. Additionally, do not encode special characters in the URL, as this may cause abnormalities in the payment process.
>
>   **Q: How should the payment result page be displayed?**
>
>  A: Follow these recommendations for display:
>
>  - You need to pass an HTTPS address via the     *paymentRedirectUrl*     parameter in the payment request to display the payment result on the merchant side.
> - If     *notRedirectAfterComplete*     is set to   `true`   when creating the Payment Element instance, and the payment method supports completion within the SDK, the payment result will be returned via   `submitPayment().then()`  . Handle the redirection logic based on the   `{code, status}`   in the response.
> - Whether the subscription creation succeeds or fails, the buyer may return to the merchant page from the payment method side. Do not set     *paymentRedirectUrl*     to a fixed "subscription payment success page." Instead, you should the display result page based on the actual results returned by the server to avoid misleading the buyer.
>
>  ​
>
>  **Q: Does returning to the merchant result page indicate that the subscription payment was created successfully?**
>
>  A: You cannot determine whether the subscription was created successfully solely based on the redirection to the merchant page. Key reasons include:
>
>  - After the buyer successfully creates a subscription payment, they may fail to return to the merchant page due to network abnormalities or other reasons.
> - Even if the buyer does not complete the subscription payment, they may still return to the merchant page via the payment method side.
> - Even if the buyer completes the subscription payment authentication, the subscription will not take effect if the initial deduction fails.

## Step 4: Obtain the authorization or payment result   **[Server-side]** {#2MxqX3}

 In the payment processing flow, Antom will send you corresponding result notifications based on the type of payment method used.

 - For card payments, [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), and [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), Antom sends authorization result notifications to inform you whether the authorization was successful. Only after a successful authorization will the fund capture be triggered. Please use the capture result as the basis for shipping goods.
- For APM payments, when the payment succeeds or fails, Antom will send you payment result notifications.

 You can obtain payment or authorization results by receiving asynchronous notifications from Antom or by proactively querying.

<!-- TabGroup -->

**Tab: Receive asynchronous notifications**

**1\. Configure the webhook URL to receive asynchronous notifications**

 When a payment succeeds or fails, Antom will send an asynchronous notification to the webhook URL you set. You can choose one of the following two methods to configure the webhook URL for receiving notifications (if both are set, the URL specified in the request takes precedence):

 - If each of your orders has a unique notification URL, it is recommended to set the webhook URL in each request. You can pass the asynchronous notification receiving URL for the specific order through     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API.
- If all your orders share a unified notification URL, you can set the webhook URL on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/developers/iNotify)   through     **Developer > Notification URL**    . For detailed steps, refer to   [Notification URL](https://docs.antom.com/ac/merchant_service/notification.md)  .

 The following code shows a sample of the asynchronous notification request:

<!-- TabGroup -->

**Tab: Card payments, Apple Pay, Google Pay**

```json
{
  "actualPaymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "cardInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cardToken":"exxxxe",
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "networkTransactionId": "XXXXX",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "cavv": "",
      "eci": ""
    }
  },
  "notifyType": "PAYMENT_RESULT",
  "paymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "paymentMethodType": "CARD",
  "paymentCreateTime": "2024-01-01T00:00:00+08:00",
  "paymentId": "20240101123456789XXXX",
  "paymentRequestId": "paymentRequestId01",
  "paymentResultInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cardToken":"exxxxe", // store cardToken for future card payments
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "networkTransactionId": "XXXXX",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "cavv": "",
      "eci": ""
    }
  },
  "paymentTime": "2024-01-01T00:01:00+08:00",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
```

 The following table shows the possible values of     *result.resultStatus*     in the notification request of payment result. Please handle the result according to the guidance provided:

   | ***result.resultStatus*** | **Message** | **Further action** |
| --- | --- | --- |
| `S` | Authorization    was successful. | You may proceed to initiate a capture. |
| `F` | Authorization    failed. | Please use a new     *paymentRequestId*        and initiate the payment again. |

**Tab: APM payments**

```json
{
    "actualPaymentAmount": {
        "currency": "HKD",
        "value": "100"
    },
    "notifyType": "PAYMENT_RESULT",
    "paymentAmount": {
        "currency": "HKD",
        "value": "100"
    },
    "paymentCreateTime": "2025-02-04T22:11:19-08:00",
    "paymentId": "20240101123456789XXXX",
    "paymentMethodType": "ALIPAY_HK",
    "paymentRequestId": "paymentRequestId01",
    "paymentResultInfo": {

    },
    "paymentTime": "2025-02-04T22:14:25-08:00",
    "pspCustomerInfo": {
        "pspCustomerId": "216022003753XXXX",
        "pspName": "ALIPAY_HK"
    },
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}
```

 The following table shows the possible values of     *result.resultStatus*     in the notification request of payment result. Please handle the result according to the guidance provided:

 | ***result.resultStatus*** | **Message** | **Further action** |
| --- | --- | --- |
| `S` | Payment was successful. | **​**    You may proceed to ship the goods. |
| `F` | Payment failed. | Please use a new     *paymentRequestId*        and initiate the payment again. |

<!-- /TabGroup -->
 **2\. Verify the asynchronous notification**

 When you receive an asynchronous notification from Antom, you are required to return the response in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)   format, but you do not need to countersign the response.

 You need to verify the signature of the payment notification sent by Antom:

 ```java
import javax.servlet.http.HttpServletRequest;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.alipay.global.api.model.Result;
import com.alipay.global.api.model.ResultStatusType;
import com.alipay.global.api.response.AlipayResponse;
import com.alipay.global.api.tools.WebhookTool;

@RestController
public class PaymentNotifyHandleBySDK {

    /**
     * alipay public key, used to verify signature
     */
    private static final String SERVER_PUBLIC_KEY = "";
  
    /**
     * payment result notify processor
     * using <a href="https://spring.io">Spring Framework</a>
     *
     * @param request    HttpServletRequest
     * @param notifyBody notify body
     * @return
     */
    @PostMapping("/payNotify")
    public Object payNotifyHandler(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");

        Result result;
        AlipayResponse response = new AlipayResponse();

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

            // deserialize the notification body
  
            // update the order status with notify result

            // respond the server that the notification is received
            result = new Result("SUCCESS", "success", ResultStatusType.S);

        } catch (Exception e) {
            String errorMsg = e.getMessage();
            // handle error condition
            result = new Result("ERROR", errorMsg, ResultStatusType.F);
        }
        response.setResult(result);
        return ResponseEntity.ok().body(response);
    }

}
```

 Whether the payment is successful or not, each notification request must be responded to in the format specified below. Otherwise, Antom will resend the asynchronous notification.

 ```json
{
    "result": {
        "resultCode": "SUCCESS",
        "resultStatus": "S",
        "resultMessage": "success"
    }
}
```

 > **[INFO]** **Common questions**
>
>  **Q: When will the notification be sent?**
>
>  A: It depends on whether the payment is completed:
>
>  - If the payment is successfully completed, Antom will send you an asynchronous notification within 3 to 5 seconds. For some payment methods like OTC, the notification might take a bit longer.
> - If the payment is not completed, Antom needs to close the order first before sending an asynchronous notification. The time it takes for different payment methods to close the order varies, usually defaulting to 14 minutes.
>
>  
>  **Q: Will the asynchronous notification be re-sent?**
>
>  A: Yes, the asynchronous notification will be re-sent automatically within 24 hours for the following cases:
>
>  - If you didn't receive the asynchronous notification due to network reasons.
> - If you receive an asynchronous notification from Antom, but you did not respond to the notification in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)      format.
>
>  The notification can be resent up to 8 times or until a correct response is received to terminate delivery. The sending intervals are as follows: 0 minutes, 2 minutes, 10 minutes, 10 minutes, 1 hour, 2 hours, 6 hours, and 15 hours.
>
>  ​
>
>  **Q: When responding to an asynchronous notification, do I need to add a digital signature?**
>
>  A: If you receive an asynchronous notification from Antom, you are required to return the response in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)   format, but you do not need to countersign the response.
>
>  ​
>
>  **Q: What key parameters do I need to use in the notification?**  
>    A: Please note the following key parameters:
>
>  - *result*    : For APM payments, it represents the final payment result. For [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), and card payments, it only represents the authorization result, and further capture is required.
> - *paymentRequestId*    : The payment request ID used for inquiries, cancellations, and reconciliation.
> - *paymentId*    : The payment order ID generated by Antom, used for refunds and reconciliation.
> - *paymentAmount*    : The payment amount.

**Tab: Inquire about the result**

You can also inquire about the payment or authorization result by calling the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API using     *paymentRequestId*     from the payment request,    regardless of whether it is an APM payment, card payment, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), or [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md).

 The following sample code shows how to call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API:

 ```java
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
    }
}
```

 The following sample code shows a request message:

 ```json
{
  "paymentRequestId": "paymentRequestId01"
}
```

 The following sample code shows a response message:

 ```json
{
  "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"
  }
}
```

 Please handle the result based on the value of the     *paymentStatus*     parameter in the response. For specific return values, refer to the   [API documentation](https://docs.antom.com/ac/ams/paymentri_online.md#Responseparameters-paymentStatus)  .

 > **[INFO]** **Common questions**
>
>  **Q: What key parameters should I pay attention to when using the**       [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)       **API to check the payment or authorization status?**
>
>  A: Please note the following key parameters:
>
>  - *result*    :    Only indicates the result of the API call. For APM payments, the final payment result should be determined based on     *paymentStatus*     (  `SUCCESS`  /  `FAIL`  /  `PROCESSING`  ). For card payments, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), and [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md),     *paymentStatus*     only represents the authorization result, and the decision to ship goods should rely on the capture result.
> - *paymentAmount*    : Used to verify the payment amount.
> - *paymentId*    : The payment order ID generated by Antom, used for refunds and reconciliation.
>
>  ​
>
>  **Q: How often should I call the inquiryPayment API?**
>
>  A: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API constantly with an interval of 2 seconds until the final payment result is obtained or an asynchronous payment result notification is received.

<!-- /TabGroup -->

## (Optional) Step 5: Capture   **[Server-side]** {#q220V5}

 > **[INFO]** **Note**    : Card payments and certain APM payment methods (such as [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Pay by Bank](https://docs.antom.com/ac/antomop/pay_by_bank.md)) must undergo a capture process, and capture will only be triggered after a successful authorization.

 After successful authorization, Antom will automatically initiate capture by default, but also supports you to initiate manual capture. Antom will send the capture result notification to you via the   [**notifyCapture (One-time Payments)**](https://docs.antom.com/ac/ams/notify_capture.md)   API, while you can also query the capture result. You need to decide whether to ship goods based on the capture result. For specific operations, refer to   [Capture](https://docs.antom.com/ac/cashierpay/capture.md)  .

## Step 6: Obtain subscription notifications   **[Server-side]** {#A2nvC}

**Tab: Android**

# User experience {#B3EWwv}

 The following figures illustrates the user experience of integrating Payment Element for the first-time subscription and subsequent deductions:

## First-time subscription {#o3Fdja}

<!-- TabGroup -->

**Tab: APM payments**

The figure below illustrates the buyer's user experience for APM payments:

 ![APM first-time subscription payment flow using Antom Payment Element for iOS terminal](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/bb97c854-32e1-4cc8-a18e-daeb89040b01.png)

**Tab: Card payments**

The figure below illustrates the buyer's user experience for card payments:

 ![Card first-time subscription payment flow using Antom Payment Element for iOS terminal](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/9205de62-ad6a-4831-a920-e49ca4711826.png)

<!-- /TabGroup -->

## Subsequent deductions {#F3h4C}

 Antom server will automatically initiate subsequent periodic deductions. The merchant server will receive subscription renewal payment notifications to renew the subscription service for the buyer. This process involves no page interaction.

# Order lifecycle {#i2YCQo}

 Learn about the lifecycle of different payment methods:

<!-- TabGroup -->

**Tab: APM Payments**

The following figure illustrates the subscription lifecycle for APM payments, including creating a subscription, signing and binding a payment method, completing the initial deduction, and initiating refunds when necessary. This process ensures the normal activation of subscriptions and secure, transparent handling of fees:

 ![Subscription lifecycle diagram for APM payments](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/428e0e06-2bad-4df7-bb44-e0c6735ff961.png)

**Tab: Card payments, Apple Pay, Google Pay**

The following figure illustrates the subscription lifecycle for card payments, [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), and [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), including creating a subscription, signing and binding a payment method, completing the initial deduction, and initiating refunds when necessary. This process ensures the normal activation of subscriptions and secure, transparent handling of fees:

 ![Subscription lifecycle diagram for card payments, Google Pay, and Apple Pay](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/c83d8e1a-edd1-4583-b156-49012af04486.png)

<!-- /TabGroup -->

 ![Subscription status and notification flow during the subscription lifecycle for Payment Element integration](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/360391dc-b3bd-41d5-80eb-de906fbe694c.png)

# Payment flow {#ni3JF}

 The following flow illustrates how to integrate Subscription Payment using Payment Element:

<!-- TabGroup -->

**Tab: APM Payments**

<!-- TabGroup -->

**Tab: First-time subscription**

![APM first-time subscription payment flow using Payment Element for iOS terminal](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/e70bd180-e7cf-4993-88f9-2af39f650cfb.png)

**Tab: Subsequent deductions**

![APM subsequent recurring deductions using Payment Element](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/21a235fb-e31a-467b-8a6e-0db536e6767e.png)

<!-- /TabGroup -->

**Tab: Card payments, Apple Pay, Google Pay**

<!-- TabGroup -->

**Tab: First-time subscription**

![Card, Apple Pay, and Google Pay first-time subscription payment flow using Payment Element for iOS terminal](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/233677b8-1cf8-4ae3-85e6-d4d77d784e1b.png)

**Tab: Subsequent deductions**

![Card, Apple Pay, and Google Pay subsequent recurring deductions using Payment Element](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2026/png/5926a000-fb1c-4fcf-b194-2397b614ee61.png)

<!-- /TabGroup -->

<!-- /TabGroup -->

<!-- TabGroup -->

**Tab: First-time subscription**

1. **The buyer enters the subscription product page and initiates payment.**  
   Merchant client collects relevant information about the buyer's subscription.
2. **Create a payment session request.**  
 Call the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)        API to obtain the payment session. You can specify one or multiple payment method types, or leave the payment method type unspecified, to submit the payment request.
3. **Invoke Payment Element.**  
   On the client side, invoke Payment Element using the payment session. You can choose to use the Payment Element-rendered payment method list, or render the payment methods by specifying them yourself. Payment Element will handle information processing, collect payment details, perform redirects, manage app invocations, display QR codes, and conduct validations based on the features of the selected payment method. After the payment is completed, depending on your configuration and the payment method features, you need to handle redirections based on the result returned by the   `onSubmitPayCallback`   method, or the system will automatically return to your result page.
4. **Obtain the authorization or payment result.**  
   You can obtain the payment or authorization results through one of the following two methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)      to set the address for receiving asynchronous notifications. When the payment is successful or expires, Antom will use   [**notifyPayment**](https://docs.antom.com/ac/ams/paymentrn_online.md)   to send asynchronous notifications to you.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the payment or authorization status.

 > **[INFO]** **Note**    : For card payments and certain APM payment methods (such as [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Pay by Bank](https://docs.antom.com/ac/antomop/pay_by_bank.md)), these payment methods follow an authorized-capture mode. The steps above only complete the authorization phase, indicates that the buyer has completed the payment using their card, and the funds are in a frozen state. To transfer the buyer's frozen funds to your account, you must integrate the capture step. The successful capture result will serve as the basis for shipping goods.

 5. **(Optional) Capture and obtain the capture result.**

 By default, Antom automatically handles fund capture on your behalf. You can also manually capture funds by calling the   [**capture (One-time Payments)**](https://docs.antom.com/ac/ams/capture.md)   API. The capture result can be obtained through one of the following methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)   to set the address for receiving asynchronous notifications. Upon capture completion, Antom will send you asynchronous notifications via the   [**notifyCapture (One-time Payments)**](https://docs.antom.com/ac/ams/notify_capture.md)   API.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the capture status.

 6. **Obtain subscription notifications.**  
   Once the subscription is activated, Antom will send initial subscription notifications and subscription renewal notifications to you.

**Tab: Subsequent deductions**

1. **Antom server initiates a deduction from the payment method.**
2. **Obtain the authorization or payment result.**  
   You can obtain the payment or authorization results through one of the following two methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)      to set the address for receiving asynchronous notifications. When the payment is successful or expires, Antom will use   [**notifyPayment**](https://docs.antom.com/ac/ams/paymentrn_online.md)   to send asynchronous notifications to you.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the payment or authorization status.

 > **[INFO]** **Note**    : For card payments and certain APM payment methods (such as [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Pay by Bank](https://docs.antom.com/ac/antomop/pay_by_bank.md)), these payment methods follow an authorized-capture mode. The steps above only complete the authorization phase, indicates that the buyer has completed the payment using their card, and the funds are in a frozen state. To transfer the buyer's frozen funds to your account, you must integrate the capture step. The successful capture result will serve as the basis for shipping goods.

 3. **(Optional) Capture and obtain the capture result.**

 For subsequent deductions, Antom automatically handles fund capture on your behalf. You can obtain the capture result through one of the following methods:

   - Asynchronous notification: Specify     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API or configure on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome)   to set the address for receiving asynchronous notifications. Upon capture completion, Antom will send you asynchronous notifications via the   [**notifyCapture (One-time Payments)**](https://docs.antom.com/ac/ams/notify_capture.md)   API.
  - Synchronous inquiry: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API to check the capture status.

 4. **Obtain subscription notifications.**  
   After the deduction is successful or fails, Antom will send you a subscription deduction notification.

<!-- /TabGroup -->

# Integration preparations {#h33KAW}

 Before you start integrating, read   [Integration guide](https://docs.antom.com/integration_guide_en.md)   and   [API overview](https://docs.antom.com/ac/ams/api_fund.md)   to understand the integration steps of the server-side API and the precautions for calling the API. Furthermore, ensure the following prerequisites are met:

 - Obtained your client ID
- Complete the key configuration
- Complete the configuration of     *paymentNotifyUrl*     to receive the asynchronous notification
- Integrate the server-side SDK package, install the server-side library, and initialize a request instance. For more details, refer to   [Server-side SDKs](https://docs.antom.com/ac/sdks/server_sdks.md)  .
- Integrate the client-side SDK package by following the steps detailed in   [Integrate the SDK package for Android](https://docs.antom.com/ac/sdks/android.md)  , and ensure to use the latest SDK version or no lower than 1.46.0.

# Integration steps {#33t0Y}

 Start your integration by taking the following steps:

 1. (Optional) Preload the SDK
2. Create a payment session
3. Invoke Payment Element
4. Obtain the authorization or payment result
5. (Optional) Capture
6. Obtain subscription notifications

## (Optional) Step 1: Preload the SDK   **[Client-side]** {#c3hKYz}

 Before creating a payment session, it is highly recommended that you preload the SDK to improve the rendering speed of the checkout page, reducing the waiting time for buyers during the payment. Follow the code example below to perform the preloading:

 ```java
 AMSPaymentElement.preload(getApplicationContext());
```

## Step 2: Create a payment session   **[Server-side]** {#K3uWal}

 Call the      [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API with order information to create a payment session and obtain the     *paymentSessionData*     required to invoke Payment Element. You can choose to either render the payment methods by specifying them yourself or use the Payment Element-rendered payment method list. It is recommended that you create a payment session after the buyer clicks the payment button. Depending on the method you selected for rendering the payment method list at the checkout page, pass the corresponding parameters when calling the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API.

 - Render the payment methods by yourself:    When rendering the payment method list yourself, you must pass the parameters for specifying payment methods listed in the table below. If Payment Element needs to collect payment details, pass the card payment parameters listed in the table below.

 - When using the Payment    Element-rendered    payment method list, you only need to pass the basic parameters listed in the table below. Payment Element renders all supported payment methods on the checkout page by default, but you can [specify payment methods](#L9xOX) to display only the options you need.

 | **Type** | **Parameter name** | **Required** | **Description** |
| --- | --- | --- | --- |
| Basic parameters ​ | *productCode* | Yes | For    Payment    Element   , the value is fixed as      `CASHIER_PAYMENT`  . |
| *productScene* | Yes | For    Payment    Element   , the value is fixed as      `ELEMENT_PAYMENT`  . |  |
| *paymentRequestId* | Yes | The unique ID assigned by the merchant to identify a payment request. |  |
| *paymentAmount* | Yes | The payment amount that the merchant requests to receive in the order currency. |  |
| *paymentRedirectUrl* | Yes | ​   The merchant page URL that the buyer is redirected to after the payment is completed. |  |
| *paymentNotifyUrl* | No | ​   The payment result notification URL, which can be passed in through the API or set as a fixed value through Antom Dashboard. If both are set, the value in the API takes precedence. |  |
| *settlementStrategy* | Yes | The settlement strategy for the payment request. |  |
| *order.referenceOrderId* | Yes | The unique ID to identify the order on the merchant side. |  |
| *order.orderDescription* | Yes | Summary description of the order. |  |
| *env.terminalType* | No | Terminal type of which the merchant service applies to. Valid values are: - `WEB`  : The client-side terminal type is a website, which is opened via a PC browser. - `WAP`  : The client-side terminal type is an H5 page, which is opened via a mobile browser. - `APP`  : The client-side terminal type is a mobile application.   > **[INFO]** **Note**    : This parameter is required for [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md). |  |
| *env.osType* | No | The operating system type from which the buyer initiated the transaction. Valid values: - `IOS`  : Apple's iOS. - `ANDROID`  : Google's Android.   > **[INFO]** **Note**    : Specify this parameter when     *env.TerminalType*     is   `WAP`   or   `APP`  . |  |
| *env.clientIp* | No | The IP address of the client device. > **[INFO]** **Note**    : This parameter is required for card payments, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md). |  |
| *subscriptionInfo.subscriptionDescription* | Yes | The description of the subscription. |  |
| *subscriptionInfo.subscriptionStartTime* | Yes | The date and time when the subscription becomes active. |  |
| *subscriptionInfo.subscriptionNotifyUrl* | Yes | The URL that is used to receive the subscription result notification. HTTPS address must be provided. |  |
| *subscriptionInfo.subscriptionExpiryTime* | No | The specific date and time when the created subscription expires. After the subscription expires, the order must be terminated. Default values for this parameter: - Card payments, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md): 7 days (calculated from the time the subscription creation request is sent). - APM payments: 80 minutes (calculated from the time the subscription creation request is sent).   > **[INFO]** **Note**    : For APM payment methods, you need to set the subscription expiration time to greater than 74 minutes. |  |
| *subscriptionInfo.periodRule* | Yes | The subscription period type, supporting four types of cycles:   `YEAR`  ,   `MONTH`  ,   `WEEK`  ,   `DAY`  . |  |
| Card payment parameters (Refer to   [Card payment features](#abqU1)  ) | *paymentFactor.captureMode* | No | Indicates the method for capturing funds after the user authorizes the payment. Valid values are: - `AUTOMATIC`  : indicates that Antom automatically captures the funds after the authorization. The same applies when the value is empty or you do not pass in this parameter. - `MANUAL`  : indicates that you manually capture the funds by calling the   [**capture (Checkout Payment)**](https://docs.antom.com/ac/ams/capture.md)   API. |
| *order.buyer* | Yes ​ | Buyer information. At least one of the following must be provided: - *order.buyer.referenceBuyerId* - *order.buyer.buyerPhoneNo* - *order.buyer.buyerEmail* |  |
| *availablePaymentMethod.paymentMethodMetaData.is3DSAuthentication* | No | Determine whether to set the transaction authentication type to 3DS based on risk and dispute history. Valid values are: - `true`  :    indicates that the transaction authentication type is    3DS authentication. - `false`  : indicates that the transaction authentication type is Non-3DS authentication. Default if empty or not provided. |  |
| *availablePaymentMethod.paymentMethodMetaData.tokenizeMode* | No | The tokenize mode. Indicates whether card information needs to be stored. When you opt to   [store card details](https://docs.antom.com/ac/pm/tokenization.md)  , the actual information is replaced with a unique and secure token that can be used for future payments. Valid values are: - `ENABLED`  : indicates that you obtain card binding authorization and want to store card information for future payments. - `DISABLED`  : indicates that you don’t need to store card information. The same applies when the value is empty or you do not specify this parameter. - `ASKFORCONSENT`  : indicates that you present a button on the page, allowing the buyer to choose whether or not they want to store the card information. |  |
| *availablePaymentMethod.paymentMethodMetaData.isCardOnFile* | No | Indicates whether the card used in this transaction has been   [previously processed and stored](https://docs.antom.com/ac/pm/cof.md)   by the merchant. Valid values are: - `true`  : The card used in the transaction has been previously processed and stored by the merchant. Set to   `true`   for stored card payments. - `false`  : Default. The card is new and has not been used previously on the merchant side. |  |
| *savedPaymentMethods.paymentMethodId* | No | Used for stored card payments.    The value of this parameter is that of     *cardToken*     obtained from the new card payment. |  |
| Parameters for specifying payment methods (Refer to   [Specify payment methods](#L9xOX)  ) | *availablePaymentMethod.paymentMethodTypeList.paymentMethodType* | No | Used to specify payment methods. You can specify one or multiple payment methods. |
| *availablePaymentMethod.paymentMethodTypeList.paymentMethodOrder* | No | Set the priority order of payment methods. |  |
| *availablePaymentMethod.paymentMethodTypeList.expressCheckout* | ​ | Specifies whether to enable the express checkout option for a payment method. Currently, only   `ALIPAY_CN`  ,   `APPLEPAY`   and   `GOOGLEPAY`   can be set as express checkout methods. Valid values are: - `true`  : Enables express checkout for this payment method. - `false`  : Disables express checkout for this payment method.   > **[INFO]** **Note**    : This parameter is currently supported only on Android and iOS. |  |

 The above parameters are the basic parameters for creating a payment session, for full parameters and additional requirements for certain payment methods refer to   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   **.**

 The following sample code shows how to call the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)        API:

 ```java
public ResponseEntity<ApiResponse> createPaymentSession(@RequestBody PaymentVO payment) {
    AlipayPaymentSessionRequest alipayPaymentSessionRequest = new AlipayPaymentSessionRequest();
    alipayPaymentSessionRequest.setProductCode(ProductCodeType.CASHIER_PAYMENT);
    alipayPaymentSessionRequest.setProductScene(ProductSceneConstants.ELEMENT_PAYMENT);

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

    // convert amount unit(in practice, amount should be calculated on your serverside)
    // For details, please refer to: <a href="https://docs.antom.com/ac/ref/cc">Usage rules of the Amount object</a>
    long amountMinorLong = Money.of(CurrencyUnit.of(payment.currency), new BigDecimal(payment.amountValue)).getAmountMinorLong();

    // set amount
    Amount amount = Amount.builder().currency(payment.currency).value(String.valueOf(amountMinorLong)).build();
    alipayPaymentSessionRequest.setPaymentAmount(amount);

    // set settlement strategy
    // replace with your existing settlement currency
    SettlementStrategy settlementStrategy = SettlementStrategy.builder().settlementCurrency("SGD").build();
    alipayPaymentSessionRequest.setSettlementStrategy(settlementStrategy);

    // 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 sdk testing order").orderAmount(amount).buyer(buyer).build();
    alipayPaymentSessionRequest.setOrder(order);

    // replace with your notify url
    // or configure your notify url here: <a href="https://dashboard.antom.com/global-payments/developers/iNotify">Notification URL</a>
    alipayPaymentSessionRequest.setPaymentNotifyUrl("http://www.yourNotifyUrl.com/payment/receivePaymentNotify");

    // replace with your redirect url
    alipayPaymentSessionRequest.setPaymentRedirectUrl(
        "http://localhost:8080/index.html?paymentRequestId=" + paymentRequestId);

    // set subscription info
    PeriodRule periodRule = PeriodRule.builder().periodCount(1).
        periodType("MONTH").build();

    List trials = new ArrayList<Trial>();

    Trial trial = Trial.builder().
        trialAmount(amount).
        trialStartPeriod(1).
        trialEndPeriod(2).
        build();

    trials.add(trial);

    SubscriptionInfo subscriptionInfo = SubscriptionInfo.builder().
        subscriptionDescription("Subscription description").
        subscriptionStartTime("2026-03-11T09:48:17+08:00").
        subscriptionEndTime("2026-11-21T09:48:17+08:00").
        periodRule(periodRule).
        trials(trials).
        subscriptionNotifyUrl("https://your.example.com/subscriptionNotify").
        subscriptionExpiryTime("2026-03-12T09:48:17+08:00").
        build();
    alipayPaymentSessionRequest.setSubscriptionInfo(subscriptionInfo);

    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));
}
```

<!-- TabGroup -->

**Tab: Merchant-rendered payment method list**

If you render the payment method list yourself, you must integrate by specifying individual payment methods. The following code shows a sample of the request message:

 ```json
{
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "USD",
      "value": "2900"
    },
    "orderDescription": "antom sdk testing order",
    "referenceOrderId": "4b085ec4-9999-4296-8f00-479e929edb2c"
  },
  "paymentAmount": {
    "currency": "USD",
    "value": "2900"
  },
  "paymentNotifyUrl": "http://www.yourNotifyUrl.com/payment/receivePaymentNotify",
  "paymentRedirectUrl": "http://google.com.my",
  "paymentRequestId": "PAYMENT_20260313102551842_AUTO",
  "productCode": "CASHIER_PAYMENT",
  "productScene": "ELEMENT_PAYMENT",
  "settlementStrategy": {
    "settlementCurrency": "USD"
  },

  "availablePaymentMethod": {
    "paymentMethodTypeList": [

      {
        "paymentMethodType": "ALIPAY_CN" // Specify payment method
      },

      {
        "paymentMethodType": "TNG"
      }

    ]
  },

  "subscriptionInfo": {
    "periodRule": {
      "periodCount": 1,
      "periodType": "MONTH"
    },
    "subscriptionDescription": "Subscription description",
    "subscriptionNotifyUrl": "https://your.example.com/subscriptionNotify",
    "subscriptionStartTime": "2026-03-11T09:48:17+08:00"

  }
}
```

**Tab: Payment Element-rendered payment method list**

If you use Payment Element-rendered payment method list, Payment Element will display all supported payment methods by default. The following code shows a sample of the request message:

 ```json
{
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "SGD",
      "value": "2900"
    },
    "orderDescription": "antom sdk testing order",
    "referenceOrderId": "4b085ec4-9999-4296-8f00-479e929edb2c"
  },
  "paymentAmount": {
    "currency": "SGD",
    "value": "2900"
  },
  "paymentNotifyUrl": "http://www.yourNotifyUrl.com/payment/receivePaymentNotify",
  "paymentRedirectUrl": "http://localhost:8080/index.html?paymentRequestId=7a6252b5-6d58-4033-882e-2eff7f262b35",
  "paymentRequestId": "7a6252b5-6d58-4033-882e-2eff7f262b35",
  "productCode": "CASHIER_PAYMENT",
  "productScene": "ELEMENT_PAYMENT",
  "settlementStrategy": {
    "settlementCurrency": "SGD"
  },
  "subscriptionInfo": {
    "periodRule": {
      "periodCount": 1,
      "periodType": "MONTH"
    },
    "subscriptionDescription": "Subscription description",
    "subscriptionEndTime": "2026-11-21T09:48:17+08:00",
    "subscriptionExpiryTime": "2026-03-12T09:48:17+08:00",
    "subscriptionNotifyUrl": "https://your.example.com/subscriptionNotify",
    "subscriptionStartTime": "2026-03-11T09:48:17+08:00",
    "trials": [
      {
        "trialAmount": {
          "$ref": "$.order.orderAmount"
        },
        "trialEndPeriod": 2,
        "trialStartPeriod": 1
      }
    ]
  }
}
```

<!-- /TabGroup -->
 The following code shows a sample of the response, which contains the following parameters:

 - *result.resultStatus*    :    The result of the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API call.
- *paymentSessionData*    : The payment session data to be returned to the client.
- *paymentSessionExpiryTime*    : The expiration time of the payment session.

 ```json
{
  "paymentSessionData": "WNoudiUNUDjHjx0oXddzWn0QrRHv52lrHFNMKc8/5uKSqNDklmQ7Nh4JGBNITqi3jgIASZkbpq15gIEQleY13A==&&SG&&188&&eyJhY3Rpb24iOnsibmVlZFZlcmlmeUFuZFJlc3VtZSI6ZmFsc2UsInNpZ25CdXR0b25EaXNwbGF5IjpmYWxzZSwic2tpcFNka1F1ZXJ5IjpmYWxzZSwidXNlclNpZ25BZ3JlZW1lbnQiOmZhbHNlfSwiY2xpZW50SWQiOiJTQU5EQk9YXzVZRVYxRzJaVjQzSzA3MjE3IiwiY29ubmVjdEZhY3RvciI6eyJlbmFibGVDb25uZWN0IjpmYWxzZX0sImVsaWdpYmxlRWFzeVBheU1hcmtldGluZyI6ZmFsc2UsImV4dGVuZEluZm8iOiJ7XCJPUEVOX01VTFRJX1BBWU1FTlRfQUJJTElUWVwiOlwidHJ1ZVwiLFwiZXhwcmVzc0NoZWNrb3V0XCI6XCJmYWxzZVwiLFwidmVyc2lvbk1hcFwiOlwie1xcXCJ3ZWJcXFwiOntcXFwiMS4xLjBcXFwiOntcXFwidGFyZ2V0V2ViVmVyaXNvblxcXCI6XFxcIjEuMS4wXFxcIn0sXFxcIjEuMi4wXFxcIjp7XFxcInRhcmdldFdlYlZlcmlzb25cXFwiOlxcXCIxLjIuMFxcXCJ9fSxcXFwiaU9TXFxcIjp7XFxcIjEuMS4wXFxcIjp7XFxcInRhcmdldFdlYlZlcmlzb25cXFwiOlxcXCIxLjEuMFxcXCJ9LFxcXCIxLjIuMFxcXCI6e1xcXCJ0YXJnZXRXZWJWZXJpc29uXFxcIjpcXFwiMS4yLjBcXFwifX0sXFxcIkFuZHJvaWRcXFwiOntcXFwiMS4xLjBcXFwiOntcXFwidGFyZ2V0V2ViVmVyaXNvblxcXCI6XFxcIjEuMS4wXFxcIn0sXFxcIjEuMi4wXFxcIjp7XFxcInRhcmdldFdlYlZlcmlzb25cXFwiOlxcXCIxLjIuMFxcXCJ9fX1cIn0iLCJuZWVkQWNjb3VudENvbmZpcm1QYWdlIjpmYWxzZSwicGF5bWVudFNlc3Npb25Db25maWciOnsicGF5bWVudE1ldGhvZENhdGVnb3J5VHlwZSI6IkFMTCIsInByb2R1Y3RTY2VuZSI6IkVMRU1FTlRfUEFZTUVOVCIsInByb2R1Y3RTY2VuZVZlcnNpb24iOiIxLjAifSwic2tpcFJlbmRlclBheW1lbnRNZXRob2QiOmZhbHNlfQ==",
  "paymentSessionExpiryTime": "2026-03-12T15:24:24+08:00",
  "paymentSessionId": "WNoudiUNUDjHjx0oXddzWn0QrRHv52lrHFNMKc8/5uIsshVVNylGbBzLF2n6JmoT",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
```

 The table below shows the possible values of     *result.resultStatus*     in the response. Please handle the result according to the guidance provided:

   | ***result.resultStatus*** | **Message** | **Further action** |
| --- | --- | --- |
| `S` | The payment session was successfully created, and     *paymentSessionData*     is returned. | Proceed to the next steps. |
| `U` | Unknown error. | Please use a new          *paymentRequestId*     and call the API again. If the issue persists, contact Antom Technical Support. |
| `F` | Failed to create the payment session. | Please check and verify whether the current API required request fields (including header fields and body fields) are correctly passed and valid. |

 > **[INFO]** **Note**    : If no response is received, it may indicate a network timeout. Please use a new     *paymentRequestId*     and call the API again. If the issue persists, contact Antom Technical Support.

 > **[INFO]** **Common questions**
>
>  **Q: Can I use Chinese characters in the value of the request parameters?**
>
>  A: To avoid incompatibility of certain payment methods, do not use Chinese characters for fields in the request.
>
>  ​
>
>  **Q: How to set the address to receive payment notification?**
>
>  A: Specify     *paymentNotifyUrl*     in the      [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)      API to receive the asynchronous notification about the payment result (  [**notifyPayment**](https://docs.antom.com/docs/ac/ams/paymentrn_online.md)  ), or configure the receiving URL in Antom Dashboard. If the URL is specified in both the request and Antom Dashboard, the value specified in the request takes precedence.
>
>  ​
>
>  **Q: Does the returned**       ***paymentSessionData***       **require processing before passing it to the client?**
>
>  A: Do not process or modify     *paymentSessionData*     in any way, as this may cause the Payment Element invocation to fail.
>
>  
>  **Q: Is 3DS authentication mandatory for the first transaction?**
>
>  A: The first transaction requires buyer participation and identity verification to ensure the security of subsequent recurring payments (where the buyer is not present). Specific requirements are as follows:
>
>  - Buyer uses card payment: Whether you or Antom collects card information, 3DS authentication must be completed. You can set     *is3DSAuthentication*     to   `true`   in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   request to enable Antom 3DS authentication.
> - Buyer uses [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md) and Antom decrypts the data: You need to set     *is3DSAuthentication*     to   `true`   to initiate a 3DS transaction for identity verification. If the decrypted data is a DPAN (Device Primary Account Number), the system will automatically proceed with the authorization process without requiring additional buyer verification.
> - Buyer uses [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and Antom decrypts the data: You can set     *is3DSAuthentication*     to   `false`   or omit this parameter, as the buyer has already completed Apple's verification process.

## Step 3: Invoke Payment Element   **[Client-side]** {#F3fpW}

 Use     *paymentSessionData*     to invoke Payment Element on your client. After the buyer clicks to submit payment, Payment Element will handle the entire flow based on the selected payment method, including displaying QR codes, redirecting to payment pages, performing 3DS authentication, and returning to the merchant’s result page.

 1. After obtaining     *paymentSessionData*     from the server, use the   `AMSPaymentElement`   class to create SDK instance.

   1. Creating the   `AMSPaymentElementConfiguration`  object includes the following parameters:

 | **Parameter name** | **Required** | **Type** | **Description** |
| --- | --- | --- | --- |
| *locale* | No | String | Used to pass in language information. Pass the corresponding value based on the region of the payment method. Valid values are listed as follows. If other values are passed, English is used by default: - `en_US`  : Default value. English - `pt_BR`  : Portuguese (Brazil) - `pt_PT`  : Portuguese - `es_ES`  : Spanish - `ko_KR`  : Korean - `zh_CN`  : Simplified Chinese - `zh_HK`  : Traditional Chinese - `ms_MY`  : Malay - `in_ID`  : Indonesian - `th_TH`  : Thai - `vi_VN`  : Vietnamese - `tl_PH`  : Filipino - `it_IT`  : Italian - `de_DE`  : German - `fr_FR`  : French - `nl_NL`  : Dutch - `ja_JP`  : Japanese - `ro`  : Romanian - `pl_PL`  : Polish - `ar_SA`  : Arabic - `tr_TR`  : Turkish - `hi_IN`  : Hindi |
| *options* | No | Key-value | Used to specify whether to use the default loading mode and sandbox environment. Valid values are: - `"sandbox", "true"`  ：Sandbox environment. - `"sandbox", "false"`  ：Default value. Production environment. - `"showLoading", "true"`  ：Default value. The default loading mode is used. - `"showLoading", "false"`  ：The default loading mode is not used.. - `"notRedirectAfterComplete"`  ：Optional. Boolean type. Used to set whether to redirect back to your page after payment completion. Valid values are:     - `false`  ：Default value. Redirects to your page after successful payment completion. The same applies if the value is empty.   - `true`  ：No redirection after payment completion. You need to manually control the subsequent payment flow based on client-side event codes.   > **[INFO]** **Note**    : The payment result event codes returned on the client-side are for reference only in page navigation. Transaction status updates must be based on the results returned by the server-side        [**notifyPayment**](https://docs.antom.com/ac/ams/paymentrn_online.md)        or   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)        API.   - `"appearance"`  : Optional. Used for custom appearance configuration. For more information, refer to  [Appearance customization](https://docs.antom.com/ac/cashierpay/appearance.md)  . It contains the following parameters:     - *theme*    : Theme color.   - *layout*    : Arrangement method.   - *variables*    : Override underlying CSS custom properties using CSS design tokens (e.g.,   `color-primary`  ) to implement theme customization. |

   2. Implement   `OnCreateComponentListener`  to handle events in the process of invoking the payment component and launching the payment page. It includes the following methods:

 | **Method** | **Required** | **Description** |
| --- | --- | --- |
| `onCreateComponentCallback` | No | A callback function for monitoring exceptions during payment component initialization. The callback parameters include the error     *code*     and     *message*    . Refer to the   [callback function event codes](https://docs.antom.com/ac/sdks/ref.md#efco9k)   for handling. |

   3. Implement   `OnSubmitPayListener`  to handle events related to the payment initiation process. It includes the following methods:

 | **Method** | **Required** | **Description** |
| --- | --- | --- |
| `onSubmitPayCallback` | No | A callback function for monitoring events during the payment process. The callback parameters include the error     *code*     and     *status*    . Refer to the   [callback function event codes](https://docs.antom.com/ac/sdks/ref.md#UOmXC)   for handling. |

 The following shows a sample code for creating the SDK instance using   `AMSPaymentElement`   :

   ```java
AMSPaymentElementConfiguration configuration = new AMSPaymentElementConfiguration();
configuration.setLocale(new Locale("en", "US"));
configuration.setOption("showLoading", "true");
configuration.setOption("sandbox", "true");
configuration.setOption("notRedirectAfterComplete", "false");
String appearance = "{\"theme\":\"night\",\"layout\":{\"type\":\"accordion\"},\"variables\":{\"content-primary\":\"#ff5b4d\"}}";
configuration.setOption("appearance", appearance);

configuration.setOnCreateComponentListener(new OnCreateComponentListener() {
    @Override
    public void onCreateComponentCallback(AMSStatusResult statusResult) {
        if (statusResult.getError() != null) {
            switch (statusResult.getError().getCode()) {
                case "UI_STATE_ERROR":
                    System.out.println("integration code error, please check the integration code");
                    break;
                case "PARAM_INVALID":
                    System.out.println("session data invalid, please check the session data");
                    break;
                case "INITIALIZE_WEB_TIMEOUT":
                    System.out.println("web app timeout, please invoke component again");
                    break;
                default:
                    System.out.println("unknown error, please contact support");
                break;
            }
        }
    }
});

configuration.setOnSubmitPayListener(new OnSubmitPayListener() {
    @Override
    public void onSubmitPayCallback(AMSStatusResult statusResult) {
        AMSStatus status = statusResult.getStatus();
        AMSResultError error = statusResult.getError();
        if (status ==  AMSStatus.PROCESSING) {
           if (error != null && error.getCode() != null) {
               if ("PAYMENT_IN_PROCESS".equals(error.getCode())) {
                   System.out.println("payment is in processing, please try polling the payment result from the server");
               } else if ("USER_CANCELED".equals(error.getCode())){
                   System.out.println("user cancelled the payment process, please try invoke createComponent again");
               } else if ("UNKNOWN_EXCEPTION".equals(error.getCode())) {
                   System.out.println("unknown exception, please contact support");
               } else if ("PAYMENT_RESULT_TIMEOUT".equals(error.getCode())){
                   System.out.println("get payment result timeout, please try polling the payment result from the server");
               }
           }
        } else if (status == AMSStatus.CANCELLED || status == AMSStatus.SUCCESS) {
            System.out.println("payment cancelled or success, do nothing");
        } else if (status == AMSStatus.FAIL) {
            if (error != null && error.getCode() != null) {
                if ("ORDER_IS_CANCELLED".equals(error.getCode())) {
                    System.out.println("the merchant has proactively canceled the order, please check on your own.");
                } else if ("ORDER_IS_CLOSED".equals(error.getCode()) || "INQUIRY_PAYMENT_SESSION_FAILED".equals(error.getCode())){
                    System.out.println("the order has timed out and is closed, please re-initiate payment using a new paymentRequestId.");
                } else {
                    System.out.println("unknown error, please contact support");
                }
            }
        }
    }
});

AMSPaymentElement amsPaymentElement = new AMSPaymentElement.Builder(this, (AMSPaymentElementConfiguration) configuration).build();

```

   2. Use the   `createComponent`      function from the instance object to invoke Payment Element:

   ```java
// paymentSessionData obtained when creating a payment session
String paymentSessionData = "exxxxe";

amsPaymentElement.createComponent(this, paymentSessionData);
```

### Unmount Payment Element {#1xGNou}

 Call the   `onDestory`   method to release SDK component resources in the following scenarios:

 - When the buyer navigates away from the checkout page, release the component resources created by the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API.
- When the buyer initiates multiple payments and the parameters in   `AMSPaymentElementConfiguration`      had changed, release the component resources previously created by the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API.

   ```java
// release SDK component resources
amsPaymentElement.onDestroy();
```

   You do not need to call   `onDestroy`  in the following scenarios, the SDK automatically releases resources (requires Android SDK version 1.33.0 or higher):

 - When the buyer initiates multiple payments and the parameters in   `AMSPaymentElementConfiguration`   remain unchanged, the SDK will automatically reclaim some resources after payment concludes to reset to the state before   `createComponent`  .

### Redirect to the merchant page {#rfgDEQ}

 The following scenarios describe how redirecting to the merchant page and returning payment results are handled. Please follow the guidelines below:

 | **"notRedirectAfterComplete"** | **Payment result** | **Recommended action** |
| --- | --- | --- |
| `true` | Success/Failure | If you set     *notRedirectAfterComplete*     to   `true`      when creating the Payment Element instance and the payment method supports completing payments within the SDK, the payment result will be returned via the   `onSubmitPayCallback`  method. You need to handle the redirect logic yourself based on the   [event code](https://docs.antom.com/ac/sdks/ref.md#UOmXC)   returned by   `onSubmitPayCallback`  . |
| `false` | Success | After payment is completed, Payment Element will automatically redirect to the paymentRedirectUrl result page you provided in the        [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API     *paymentRedirectUrl*     . |
| `false` | Failure | The payment result will be returned via the   `onSubmitPayCallback`  method. You need to handle the redirect logic yourself based on the   [event code](https://docs.antom.com/ac/sdks/ref.md#UOmXC)   returned by   `onSubmitPayCallback`  . |

 > **[INFO]** **Note**    :
>
>  - If the payment requires redirecting to an external page (the payment method does not support payment within the SDK), the payment result will not be returned via   `onSubmitPayCallback`  . After completing the payment on the external payment page, the payment method will determine whether to automatically redirect back to the     *paymentRedirectUrl*    you provided.
> - The payment results returned by the   `onSubmitPayCallback`      method are only for client-side page navigation and status display. For the final order status, please obtain it through   [Step 4: Obtain the authorization or payment result](#3xqX3)  .

 > **[INFO]** **Common questions**
>
>  **Q: What should be noted when passing the**       ***paymentRedirectUrl***       **parameter?**
>
>  A:     *paymentRedirectUrl*     must be set to an HTTPS address. Additionally, do not encode special characters in the URL, as this may cause abnormalities in the payment process.
>
>   **Q: How should the payment result page be displayed?**
>
>  A: Follow these recommendations for display:
>
>  - You need to pass an HTTPS address via the     *paymentRedirectUrl*     parameter in the payment request to display the payment result on the merchant side.
> - If     *notRedirectAfterComplete*     is set to   `true`   when creating the Payment Element instance, and the payment method supports completion within the SDK, the payment result will be returned via   `submitPayment().then()`  . Handle the redirection logic based on the   `{code, status}`   in the response.
> - Whether the subscription creation succeeds or fails, the buyer may return to the merchant page from the payment method side. Do not set     *paymentRedirectUrl*     to a fixed "subscription payment success page." Instead, you should the display result page based on the actual results returned by the server to avoid misleading the buyer.
>
>  ​
>
>  **Q: Does returning to the merchant result page indicate that the subscription payment was created successfully?**
>
>  A: You cannot determine whether the subscription was created successfully solely based on the redirection to the merchant page. Key reasons include:
>
>  - After the buyer successfully creates a subscription payment, they may fail to return to the merchant page due to network abnormalities or other reasons.
> - Even if the buyer does not complete the subscription payment, they may still return to the merchant page via the payment method side.
> - Even if the buyer completes the subscription payment authentication, the subscription will not take effect if the initial deduction fails.

## Step 4: Obtain the authorization or payment result   **[Server-side]** {#3xqX3}

 In the payment processing flow, Antom will send you corresponding result notifications based on the type of payment method used.

 - For card payments, [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), and [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), Antom sends authorization result notifications to inform you whether the authorization was successful. Only after a successful authorization will the fund capture be triggered. Please use the capture result as the basis for shipping goods.
- For APM payments, when the payment succeeds or fails, Antom will send you payment result notifications.

 You can obtain payment or authorization results by receiving asynchronous notifications from Antom or by proactively querying.

<!-- TabGroup -->

**Tab: Receive asynchronous notifications**

**1\. Configure the webhook URL to receive asynchronous notifications**

 When a payment succeeds or fails, Antom will send an asynchronous notification to the webhook URL you set. You can choose one of the following two methods to configure the webhook URL for receiving notifications (if both are set, the URL specified in the request takes precedence):

 - If each of your orders has a unique notification URL, it is recommended to set the webhook URL in each request. You can pass the asynchronous notification receiving URL for the specific order through     *paymentNotifyUrl*     in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API.
- If all your orders share a unified notification URL, you can set the webhook URL on   [Antom Dashboard](https://dashboard.alipay.com/global-payments/developers/iNotify)   through     **Developer > Notification URL**    . For detailed steps, refer to   [Notification URL](https://docs.antom.com/ac/merchant_service/notification.md)  .

 The following code shows a sample of the asynchronous notification request:

<!-- TabGroup -->

**Tab: Card payments, Apple Pay, Google Pay**

```json
{
  "actualPaymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "cardInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cardToken":"exxxxe",
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "networkTransactionId": "XXXXX",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "cavv": "",
      "eci": ""
    }
  },
  "notifyType": "PAYMENT_RESULT",
  "paymentAmount": {
    "currency": "SGD",
    "value": "4200"
  },
  "paymentMethodType": "CARD",
  "paymentCreateTime": "2024-01-01T00:00:00+08:00",
  "paymentId": "20240101123456789XXXX",
  "paymentRequestId": "paymentRequestId01",
  "paymentResultInfo": {
    "avsResultRaw": "A",
    "cardBrand": "MASTERCARD",
    "cardNo": "****************",
    "cardToken":"exxxxe", // store cardToken for future card payments
    "cvvResultRaw": "Y",
    "funding": "DEBIT",
    "issuingCountry": "US",
    "networkTransactionId": "XXXXX",
    "paymentMethodRegion": "GLOBAL",
    "threeDSResult": {
      "cavv": "",
      "eci": ""
    }
  },
  "paymentTime": "2024-01-01T00:01:00+08:00",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}
```

 The following table shows the possible values of     *result.resultStatus*     in the notification request of payment result. Please handle the result according to the guidance provided:

   | ***result.resultStatus*** | **Message** | **Further action** |
| --- | --- | --- |
| `S` | Authorization    was successful. | You may proceed to initiate a capture. |
| `F` | Authorization    failed. | Please use a new     *paymentRequestId*        and initiate the payment again. |

**Tab: APM payments**

```json
{
    "actualPaymentAmount": {
        "currency": "HKD",
        "value": "100"
    },
    "notifyType": "PAYMENT_RESULT",
    "paymentAmount": {
        "currency": "HKD",
        "value": "100"
    },
    "paymentCreateTime": "2025-02-04T22:11:19-08:00",
    "paymentId": "20240101123456789XXXX",
    "paymentMethodType": "ALIPAY_HK",
    "paymentRequestId": "paymentRequestId01",
    "paymentResultInfo": {

    },
    "paymentTime": "2025-02-04T22:14:25-08:00",
    "pspCustomerInfo": {
        "pspCustomerId": "216022003753XXXX",
        "pspName": "ALIPAY_HK"
    },
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}
```

 The following table shows the possible values of     *result.resultStatus*     in the notification request of payment result. Please handle the result according to the guidance provided:

 | ***result.resultStatus*** | **Message** | **Further action** |
| --- | --- | --- |
| `S` | Payment was successful. | **​**    You may proceed to ship the goods. |
| `F` | Payment failed. | Please use a new     *paymentRequestId*        and initiate the payment again. |

<!-- /TabGroup -->
 **2\. Verify the asynchronous notification**

 When you receive an asynchronous notification from Antom, you are required to return the response in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)   format, but you do not need to countersign the response.

 You need to verify the signature of the payment notification sent by Antom:

 ```java
import javax.servlet.http.HttpServletRequest;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.alipay.global.api.model.Result;
import com.alipay.global.api.model.ResultStatusType;
import com.alipay.global.api.response.AlipayResponse;
import com.alipay.global.api.tools.WebhookTool;

@RestController
public class PaymentNotifyHandleBySDK {

    /**
     * alipay public key, used to verify signature
     */
    private static final String SERVER_PUBLIC_KEY = "";
  
    /**
     * payment result notify processor
     * using <a href="https://spring.io">Spring Framework</a>
     *
     * @param request    HttpServletRequest
     * @param notifyBody notify body
     * @return
     */
    @PostMapping("/payNotify")
    public Object payNotifyHandler(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");

        Result result;
        AlipayResponse response = new AlipayResponse();

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

            // deserialize the notification body
  
            // update the order status with notify result

            // respond the server that the notification is received
            result = new Result("SUCCESS", "success", ResultStatusType.S);

        } catch (Exception e) {
            String errorMsg = e.getMessage();
            // handle error condition
            result = new Result("ERROR", errorMsg, ResultStatusType.F);
        }
        response.setResult(result);
        return ResponseEntity.ok().body(response);
    }

}
```

 Whether the payment is successful or not, each notification request must be responded to in the format specified below. Otherwise, Antom will resend the asynchronous notification.

 ```json
{
    "result": {
        "resultCode": "SUCCESS",
        "resultStatus": "S",
        "resultMessage": "success"
    }
}
```

 > **[INFO]** **Common questions**
>
>  **Q: When will the notification be sent?**
>
>  A: It depends on whether the payment is completed:
>
>  - If the payment is successfully completed, Antom will send you an asynchronous notification within 3 to 5 seconds. For some payment methods like OTC, the notification might take a bit longer.
> - If the payment is not completed, Antom needs to close the order first before sending an asynchronous notification. The time it takes for different payment methods to close the order varies, usually defaulting to 14 minutes.
>
>  
>  **Q: Will the asynchronous notification be re-sent?**
>
>  A: Yes, the asynchronous notification will be re-sent automatically within 24 hours for the following cases:
>
>  - If you didn't receive the asynchronous notification due to network reasons.
> - If you receive an asynchronous notification from Antom, but you did not respond to the notification in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)      format.
>
>  The notification can be resent up to 8 times or until a correct response is received to terminate delivery. The sending intervals are as follows: 0 minutes, 2 minutes, 10 minutes, 10 minutes, 1 hour, 2 hours, 6 hours, and 15 hours.
>
>  ​
>
>  **Q: When responding to an asynchronous notification, do I need to add a digital signature?**
>
>  A: If you receive an asynchronous notification from Antom, you are required to return the response in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)   format, but you do not need to countersign the response.
>
>  ​
>
>  **Q: What key parameters do I need to use in the notification?**  
>    A: Please note the following key parameters:
>
>  - *result*    : For APM payments, it represents the final payment result. For [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), and card payments, it only represents the authorization result, and further capture is required.
> - *paymentRequestId*    : The payment request ID used for inquiries, cancellations, and reconciliation.
> - *paymentId*    : The payment order ID generated by Antom, used for refunds and reconciliation.
> - *paymentAmount*    : The payment amount.

**Tab: Inquire about the result**

You can also inquire about the payment or authorization result by calling the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API using     *paymentRequestId*     from the payment request,    regardless of whether it is an APM payment, card payment, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), or [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md).

 The following sample code shows how to call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API:

 ```java
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
    }
}
```

 The following sample code shows a request message:

 ```json
{
  "paymentRequestId": "paymentRequestId01"
}
```

 The following sample code shows a response message:

 ```json
{
  "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"
  }
}
```

 Please handle the result based on the value of the     *paymentStatus*     parameter in the response. For specific return values, refer to the   [API documentation](https://docs.antom.com/ac/ams/paymentri_online.md#Responseparameters-paymentStatus)  .

 > **[INFO]** **Common questions**
>
>  **Q: What key parameters should I pay attention to when using the**       [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)       **API to check the payment or authorization status?**
>
>  A: Please note the following key parameters:
>
>  - *result*    :    Only indicates the result of the API call. For APM payments, the final payment result should be determined based on     *paymentStatus*     (  `SUCCESS`  /  `FAIL`  /  `PROCESSING`  ). For card payments, [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), and [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md),     *paymentStatus*     only represents the authorization result, and the decision to ship goods should rely on the capture result.
> - *paymentAmount*    : Used to verify the payment amount.
> - *paymentId*    : The payment order ID generated by Antom, used for refunds and reconciliation.
>
>  ​
>
>  **Q: How often should I call the inquiryPayment API?**
>
>  A: Call the   [**inquiryPayment**](https://docs.antom.com/ac/ams/paymentri_online.md)   API constantly with an interval of 2 seconds until the final payment result is obtained or an asynchronous payment result notification is received.

<!-- /TabGroup -->

## (Optional) Step 5: Capture   **[Server-side]** {#q330V5}

 > **[INFO]** **Note**    : Card payments and certain APM payment methods (such as [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) and [Pay by Bank](https://docs.antom.com/ac/antomop/pay_by_bank.md)) must undergo a capture process, and capture will only be triggered after a successful authorization.

 After successful authorization, Antom will automatically initiate capture by default, but also supports you to initiate manual capture. Antom will send the capture result notification to you via the   [**notifyCapture (One-time Payments)**](https://docs.antom.com/ac/ams/notify_capture.md)   API, while you can also query the capture result. You need to decide whether to ship goods based on the capture result. For specific operations, refer to   [Capture](https://docs.antom.com/ac/cashierpay/capture.md)  .

## Step 6: Obtain subscription notifications   **[Sever-side]** {#A3nvC}

<!-- /ToggleTab -->
 After the subscription takes effect, Antom will send you the following notifications:

 - [First-time subscription notification](#OZj5u)
- [Subscription renewal notification](#tpXaK)

### First-time subscription notification {#OZj5u}

 Antom will send the following event notifications via HTTPS to the webhook configured in the API or Antom Dashboard:

 | **Notification type** | **API** | **Request message** |
| --- | --- | --- |
| Subscription status notification | After the subscription takes effect, Antom will use the      [**notifySubscription**](https://docs.antom.com/ac/ams/notify_sub.md)   API to send subscription result notifications to you. | - When the     *subscriptionNotificationType*     in the request message is        `CREATE`  ,    *subscriptionStatus*     determines whether the subscription is active (  `ACTIVE`  ) or terminated  (  `TERMINATED`  ). - When the     *subscriptionNotificationType*     in the request message is   `TERMINATE`   , the subscription is terminated. |
| Current deduction result notification | After the subscription payment is successfully activated or renewed, Antom will use the   [**notifyPayment**](https://docs.antom.com/ac/ams/notify_subpayment.md)   API to send you the current subscription deduction result. | When the          *notifyType*          in the request message is   `PAYMENT_RESULT`  ,     *result.resultStatus*     determines whether the current deduction was successful (  `S`  ) or failed (  `F`  ). |

<!-- TabGroup -->

**Tab: Subscription status notification**

Follow the steps below to obtain subscription status notifications:

 1. Set the webhook URL for receiving notifications: Configure the     *subscriptionInfo.subscriptionNotifyUrl*     parameter in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API. Below are examples of two types of subscription status notifications:

   - When the value of     *subscriptionNotificationType*     is   `CREATE`  , determine the subscription status based on the value of      *subscriptionStatus*     :

     - `ACTIVE`  : Indicates the subscription is active.
    - `TERMINATED`  : Indicates the subscription is terminated.

 ```json
{
	"periodRule": {
		"periodCount": 1,
		"periodType": "MONTH"
	},
	"subscriptionEndTime": "2074-02-20T01:16:17-08:00",
	"subscriptionId": "20240914190000000000000050000010226",
	"subscriptionNotificationType": "CREATE",
	"subscriptionRequestId": "SUBSCRIPTION_202444091410165oo009851_AUTO",
	"subscriptionStartTime": "2024-09-13T19:30:17-07:00",
	"subscriptionStatus": "ACTIVE"
}
```

   - When the value of     *subscriptionNotificationType*     is   `TERMINATE`   , the subscription is terminated.

 ```json
{
  "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"
}
```

 2. Verify the asynchronous notification

 When you receive an asynchronous notification from Antom, you are required to return the response in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)  format, but you do not need to countersign the response.

 You need to verify the signature of the payment notification sent by Antom:

 ```java
/**
     * Receive subscription notification
     *
     * @param request    request
     * @param notifyBody notify body
     * @return Result
     */
@PostMapping("/receiveSubscriptionNotify")
@ResponseBody
public Result receiveSubscriptionNotify(HttpServletRequest request, @RequestBody String notifyBody) {
    // Get required parameters from HTTP request
    String requestUri = request.getRequestURI();
    String requestMethod = request.getMethod();

    // Get necessary parameters from request headers
    String requestTime = request.getHeader("request-time");
    String clientId = request.getHeader("client-id");
    String signature = request.getHeader("signature");

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

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

        if (subscriptionNotify != null && SubscriptionNotificationType.CREATE.equals(subscriptionNotify.getSubscriptionNotificationType())) {
            // Process your business logic
            // For example: Save user subscription information through the relationship between subscriptionRequestId and 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();
}
```

 3. Whether the payment is successful or not, each notification request must be responded to in the format specified below. Otherwise, Antom will resend the asynchronous notification.

 ```json
{
  "result": {
    "resultCode": "SUCCESS",
    "resultStatus": "S",
    "resultMessage": "success"
  }
}
```

**Tab: Current deduction result notification**

Obtain the current deduction result notification by following the steps below:

 1. Set the webhook URL to receive notifications: Configure through the     *paymentNotifyUrl*        parameter in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/docs/ac/ams/session_cashier.md)   API or configure the webhook URL in Antom Dashboard.

 The following is an example of a payment result notification:

 ```json
{
  "paymentAmount": {
    "currency": "USD",
    "value": "1"
  },
  "notifyType": "PAYMENT_RESULT",
  "paymentCreateTime": "2025-10-08T19:10:51-07:00",
  "paymentId": "202510091940108001001889B0255128143",
  "paymentTime": "2025-10-08T19:11:40-07:00",
  "periodEndTime": "2025-08-02T19:15:29-07:00",
  "periodStartTime": "2025-07-26T19:15:29-07:00",
  "phaseNo": "1",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success",
    "resultStatus": "S"
  },
  "subscriptionId": "202510091900000000000000E0000032897",
  "subscriptionRequestId": "PAYMENT_20251009101050687_AUTO"
}
```

 Description of key parameters:

   - *notifyType*    : Notification type; the value is      `PAYMENT_RESULT`  .
  - *phaseNo*    : The sequence number of the current subscription cycle.
  - *periodStartTime*    : The start time of the current subscription cycle.
  - *periodEndTime*    : The end time of the current subscription cycle.
  - *paymentAmount*    : The charged amount for each subscription cycle.

 2. Verify the asynchronous notification

 When you receive an asynchronous notification from Antom, you are required to return the response in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)   format, but you do not need to countersign the response.

 You need to verify the signature of the payment notification sent by Antom:

 ```java
/**
     * receive payment notify
     *
     * @param request    request
     * @param notifyBody notify body
     * @return Result
     */
    @PostMapping("/receivePaymentNotify")
    @ResponseBody
    public Result receivePaymentNotify(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("通知签名无效");
            }

            // deserialize the notification body
            AlipaySubscriptionPayNotify paymentNotify = JSON.parseObject(notifyBody, AlipaySubscriptionPayNotify.class);

            if (paymentNotify != null && "SUCCESS".equals(paymentNotify.getResult().getResultCode())) {
                // handle your own business logic.
                // e.g. The payment information of the user is saved through the relationship between the subscriptionRequestId and the user ID.
                System.out.println("接收到支付通知: " + 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();
    }
```

 3. Whether the payment is successful or not, each notification request must be responded to in the format specified below. Otherwise, Antom will resend the asynchronous notification.

 ```json
{
  "result": {
    "resultCode": "SUCCESS",
    "resultStatus": "S",
    "resultMessage": "success"
  }
}
```

<!-- /TabGroup -->

 > **[INFO]** **Common questions**
>
>  **Q: Will the asynchronous notification be re-sent?**
>
>  A: Yes, the asynchronous notification will be re-sent automatically within 24 hours for the following cases:
>
>  - If you didn't receive the asynchronous notification due to network reasons.
> - If you receive an asynchronous notification from Antom, but you did not respond to the notification in the   [Sample code](https://docs.antom.com/ac/cashierpay/notifications.md#vQK5A)      format.
>
>  The notification can be resent up to 8 times or until a correct response is received to terminate delivery. The sending intervals are as follows: 0 minutes, 2 minutes, 10 minutes, 10 minutes, 1 hour, 2 hours, 6 hours, and 15 hours.
>
>  ​
>
>  **Q: Do I need to verify the signature after receiving the payment result notification?**
>
>  A: Yes. To ensure that the callback request is indeed sent by Antom, verification is required during signature verification. When assembling messages to be verified, y   ou are required to process in the following standard order:   `<http-method> <http-uri> <client-id>.<request-time>.<request-body>`  . Note that   `<request-body>`  should be assembled directly from the original value, rather than parsed into JSON and then assembled.
>
>  ​
>
>  **Q: If the first payment fails, will the subscription take effect?**
>
>  A: When the first deduction during subscription activation fails, the subscription will not take effect. Antom will push a webhook notification with     *subscriptionStatus=*   `TERMINATED`  to you, indicating that the subscription activation has failed.
>
>  **Q: What is the subscription timeout period?**
>
>  For APM payment types, the default timeout period is 30 minutes. For card payment types, the default timeout period is 7 days.

### Subscription renewal notification {#tpXaK}

 After the subscription is successfully created and has taken effect, the Antom system will automatically initiate renewal deductions based on the subscription rules you have configured, and send the corresponding payment result notifications through webhook to achieve periodic deductions. The timing and rules for triggering renewal deductions are as follows:

 - Trigger timing: The renewal deduction will be automatically triggered 24 hours before the start date of the next subscription cycle. You can determine the initiation time of the next cycle’s payment by subtracting 24 hours from the     *periodEndTime*     parameter in the previous cycle’s payment result notification.
- Cycle rule: The renewal period and deduction frequency will follow the     *periodRule*     defined during subscription creation, for example, daily, monthly, quarterly, or yearly billing. The explanation is as follows:

 | **First payment deduction time** **(**      ***paymentTime***       **=**       ***periodStartTime***      **)** | **Subscription cycle** | **Next payment deduction time**      **(**      ***paymentTime***      **)** | **Next period effective time**      **(**      ***periodStartTime***      **)** |
| --- | --- | --- | --- |
| 2024-09-25T20:10:17+08:00 | Set the subscription cycle to 3 days, you need to pass in the following parameters: - *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 | Set the subscription cycle to one week, you need to pass in the following parameters: - *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 | Set the subscription cycle to one month, you need to pass in the following parameters: - *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 | Set the subscription cycle to one year, you need to pass in the following parameters: - *periodRule.periodCount*     =       `1` - *periodRule.periodType*         =   `YEAR` | 2025-09-24T20:10:17+08:00 | 2025-09-25T20:10:17+08:00 |

<!-- TabGroup -->

**Tab: Card payments, Apple Pay, Google Pay**

For card payments, [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md), and [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) payment scenarios, asynchronous notifications are typically divided into the following scenarios:

 | **Common scenarios** | **First-time subscription payment** | **Subsequent recurring payments** |
| --- | --- | --- |
| Authorized payment result notification | A notification is sent to inform you of the authorization result and the 3DS authentication result. | A notification is sent to inform you of the authorization result and the 3DS authentication result. |
| Capture notification | If authorized payment is successful, a capture notification is sent. The capture result serves as the basis for shipment. | If authorized payment is successful, a capture notification is sent. The capture result serves as the basis for shipment. |
| Subscription status notification | A notification is sent to inform you of the subscription creation result. | No notification. |
| Current deduction result notification | A notification is sent to inform you of the current deduction result, amount, and validity period. | A notification is sent to inform you of the current deduction result, amount, and validity period. |

 The following are sample codes for asynchronous notifications in each scenario:

<!-- TabGroup -->

**Tab: Authorized payment result notification**

The following code shows a sample of the authorized payment result notification:

 ```json
{
	"actualPaymentAmount": {
		"currency": "USD",
		"value": "1"
	},
	"cardInfo": {
		"avsResultRaw": "U",
		"cardBrand": "VISA",
		"cardCategory": "CONSUMER",
		"cardNo": "************0550",
		"credentialTypeUsed": "PAN",
		"cvvResultRaw": "M",
		"funding": "PREPAID",
		"issuingCountry": "MY",
		"networkTransactionId": "202510090310009b937936376",
		"paymentMethodRegion": "GLOBAL",
		"threeDSResult": {
			"cavv": "mockCavv",
			"challengeCancel": "",
			"challenged": true,
			"dsTransactionId": "cce18b6e-b55e-40ff-814c-619b45987636",
			"eci": "02",
			"threeDSOffered": true,
			"threeDSVersion": "2.1.0",
			"threeDStransactionStatusReason": ""
		}
	},
	"notifyType": "PAYMENT_RESULT",
	"paymentAmount": {
		"currency": "USD",
		"value": "1"
	},
	"paymentCreateTime": "2025-10-08T19:11:20-07:00",
	"paymentId": "202510091940108001001889B0255128143",
	"paymentMethodType": "GOOGLEPAY",
	"paymentRequestId": "PAYMENT_20251009101050687_AUTO",
	"paymentResultInfo": {
		"avsResultRaw": "U",
		"cardBrand": "VISA",
		"cardCategory": "CONSUMER",
		"cardNo": "************0550",
		"credentialTypeUsed": "PAN",
		"cvvResultRaw": "M",
		"funding": "PREPAID",
		"issuingCountry": "MY",
		"networkTransactionId": "202510090310009b937936376",
		"paymentMethodRegion": "GLOBAL",
		"threeDSResult": {
			"cavv": "mockCavv",
			"challengeCancel": "",
			"challenged": true,
			"dsTransactionId": "cce18b6e-b55e-40ff-814c-619b45987636",
			"eci": "02",
			"threeDSOffered": true,
			"threeDSVersion": "2.1.0",
			"threeDStransactionStatusReason": ""
		}
	},
	"paymentTime": "2025-10-08T19:11:38-07:00",
	"result": {
		"resultCode": "SUCCESS",
		"resultMessage": "success.",
		"resultStatus": "S"
	}
}
```

**Tab: Capture notification**

The following code shows a sample of the capture notification:

 ```json
{
	"captureAmount": {
		"currency": "USD",
		"value": "1"
	},
	"captureId": "202510091940108070001889B0280060023",
	"captureRequestId": "PAYMENT_20251009101050687_AUTO",
	"captureTime": "2025-10-08T19:11:40-07:00",
	"notifyType": "CAPTURE_RESULT",
	"paymentId": "202510091940108001001889B0255128143",
	"result": {
		"resultCode": "SUCCESS",
		"resultMessage": "success.",
		"resultStatus": "S"
	}
}
```

**Tab: Subscription status notification**

No notification.

**Tab: Current deduction result notification**

The following code shows a sample of the current deduction result notification:

 ```json
{
	"paymentAmount": {
		"currency": "USD",
		"value": "1"
	},
    "notifyType": "PAYMENT_RESULT",
	"paymentCreateTime": "2025-10-08T19:10:51-07:00",
	"paymentId": "202510091940108001001889B0255128143",
	"paymentTime": "2025-10-08T19:11:40-07:00",
	"periodEndTime": "2025-08-02T19:15:29-07:00",
	"periodStartTime": "2025-07-26T19:15:29-07:00",
	"phaseNo": "1",
	"result": {
		"resultCode": "SUCCESS",
		"resultMessage": "success",
		"resultStatus": "S"
	},
	"subscriptionId": "202510091900000000000000E0000032897",
	"subscriptionRequestId": "PAYMENT_20251009101050687_AUTO"
}
```

 Description of key parameters:

   - *notifyType*    : Notification type; the value is      `PAYMENT_RESULT`  .
  - *phaseNo*    : The sequence number of the current subscription cycle.
  - *periodStartTime*    : The start time of the current subscription cycle.
  - *periodEndTime*    : The end time of the current subscription cycle.
  - *paymentAmount*    : The charged amount for each subscription cycle.

<!-- /TabGroup -->

**Tab: APM payments**

In APM payment scenarios, asynchronous notifications are typically categorized into the following cases:

 | **Common scenarios** | **First-time subscription payment** | **Subsequent recurring payments** |
| --- | --- | --- |
| Payment result notification | A notification is sent to inform you of the payment result. | No notification. |
| Subscription status notification | A notification is sent to inform you of the subscription creation result. | No notification. |
| Current deduction result notification | A notification is sent to inform you of the current deduction result, amount, and validity period. | A notification is sent to inform you of the current deduction result, amount, and validity period. |

 The following are sample codes for asynchronous notifications in each scenario:   ​

<!-- TabGroup -->

**Tab: Payment result notification**

No notification.

**Tab: Subscription status notification**

No notification.

**Tab: Current deduction result notification**

The following code shows a sample of the current deduction result notification:

 ```json
{
	"paymentAmount": {
		"currency": "USD",
		"value": "1"
	},
    "notifyType": "PAYMENT_RESULT",
	"paymentCreateTime": "2025-10-08T19:10:51-07:00",
	"paymentId": "202510091940108001001889B0255128143",
	"paymentTime": "2025-10-08T19:11:40-07:00",
	"periodEndTime": "2025-08-02T19:15:29-07:00",
	"periodStartTime": "2025-07-26T19:15:29-07:00",
	"phaseNo": "1",
	"result": {
		"resultCode": "SUCCESS",
		"resultMessage": "success",
		"resultStatus": "S"
	},
	"subscriptionId": "202510091900000000000000E0000032897",
	"subscriptionRequestId": "PAYMENT_20251009101050687_AUTO"
}
```

 Description of key parameters:

   - *notifyType*    : Notification type; the value is      `PAYMENT_RESULT`  .
  - *phaseNo*    : The sequence number of the current subscription cycle.
  - *periodStartTime*    : The start time of the current subscription cycle.
  - *periodEndTime*    : The end time of the current subscription cycle.
  - *paymentAmount*    : The charged amount for each subscription cycle.

<!-- /TabGroup -->

<!-- /TabGroup -->

 > **[INFO]** **Common questions**
>
>  **Q: If a payment deduction fails, will it cause the subscription to become invalid?**
>
>  A: If the first deduction when creating a subscription fails, the subscription will not take effect. However, if the subscription has already taken effect and subsequent cycle deductions fail (for example, due to insufficient balance), the subscription will remain valid. If the subscription is not actively canceled, Antom will retry the deduction in the next billing cycle.
>
>  ​
>
>  **Q: Will a payment failure trigger a deduction notification, and will it be retried?**
>
>  A: A deduction failure notification will be sent when the deduction fails. In card subscription payment scenarios, Antom will not initiate a retry. If you need to initiate a retry, please contact Antom technical support to confirm the solution.
>
>  ​
>
>  **Q: If**      **the first deduction occurs on dates such as February 28, March 31, or April 30 (the end of the month), how is the next deduction date determined?**
>
>  A: The subscription cycle logic is based on the selected date. If the next cycle does not have that same date, the deduction will occur one day earlier. For example:
>
>  - First cycle: 1.28, Second cycle: 2.28, Third cycle: 3.28, Fourth cycle: 4.28.
> - First cycle: 1.31, Second cycle: 2.28, Third cycle: 3.3   1, Fourth cycle: 4.30.
> - First cycle: 1.30, Second cycle: 2.28, Third cycle: 3.30, Fourth cycle: 4.30.
>
>  ​
>
>  **Q: How are subsequent recurring deductions associated with the initial subscription contract?**
>
>  A: For recurring deduction notifications, the association can be made using the     *subscriptionRequestId*     or     *subscriptionId*     included in the notification request to link back to the initial contract. For recurring deductions in card payment scenarios, Antom will additionally send authorization result notifications and capture result notifications. These two notifications can be linked to the recurring deduction notification through     *paymentId*    , and ultimately associated with the initial contract’s     *subscriptionId*    .

# After subscription {#BJbEu}

 After completing the payment, you can perform the following actions:

## Subscription trial    **[Server-side]** {#Rue5J}

 Antom provides a subscription trial feature that allows buyers to experience a product or service for a limited time at no cost or at a discounted rate before officially purchasing a subscription plan. For more details, refer to    [Subscription trial](https://docs.antom.com/ac/subscriptionpay/subscription_trial.md)  .

## Subscription cancellation    **[Server-side]** {#jcX2m}

 The subscription cancellation feature allows buyers to cancel their current subscription at any time when they no longer need to use the associated service. For more details, refer to    [Subscription cancellation](https://docs.antom.com/ac/subscriptionpay/subscription_cancellation.md)  .

## Cancellation    **[Server-side]** {#Otb6j}

 For successful payments, if the buyer requests cancellation or a refund on the same day, you can use Antom’s cancellation capability to cancel the order or release funds. Orders not yet completed can also be cancelled directly. For details, refer to   [Cancel](https://docs.antom.com/ac/cashierpay/cancel.md)  .

## Refund    **[Server-side]** {#kg4oU}

 For successfully paid orders, if you need to issue a refund to the buyer, Antom provides two methods:

 - Your operations staff can manually process refunds directly through   [Antom Dashboard](https://dashboard.alipay.com/)  .
- Call the   [**refund**](https://docs.antom.com/ac/ams/refund_online.md)   API to initiate a refund.

 Antom’s refund capabilities are as follows:

 - Supports full refunds.
- Supports multiple partial refunds, with the total refunded amount not exceeding the captured amount.

 Refer to   [Refund](https://docs.antom.com/ac/cashierpay/refund.md)   to learn about Antom refund rules and operation process.

## Dispute    **[Server-side]** {#AoEw6}

 Antom provides dispute resolution services for contested transactions. For more information, refer to   [Dispute Guidance](https://docs.antom.com/ac/dispute/overview.md)  .

## Reconciliation      **[Server-side]** {#Ftc87}

 After the transaction is completed, use the financial reports provided by Antom for reconciliation. For more information on how to reconcile and the settlement rules of Antom, please refer to   [Reconciliation](https://docs.antom.com/ac/cashierpay/reconcile.md)  .

# Payment method features {#RvQap}

 This section explains differences in supported features across payment methods.

## Default timeout {#v6YBS}

 Default order timeout varies by payment method. For specifics, refer to   [Default timeout](https://docs.antom.com/ac/pm/supported_pm.md#ju3pe)  .

 > **[INFO]** **Note**    : Since Payment Element orders have a default validity period of 1 hour, the actual timeout period is extended by 1 hour beyond the payment method’s default timeout. For example, if a payment method’s default timeout is 14 minutes, the actual timeout period for a Payment Element order will be 1 hour and 14 minutes.

## Integration key points {#l5xra}

 Below are key integration points and recommended approaches for different payment methods.

 | **Payment method** | **Key points** | **Recommended solution** |
| --- | --- | --- |
| [PayPay](https://docs.antom.com/ac/antomop/paypay_mdx.md) | - *paymentRedirectUrl*     should be no longer than 255 characters. - Refunds for an order cannot exceed 20 times. | *paymentRedirectUrl*     should be no longer than 255 characters. |
| [QRIS](https://docs.antom.com/ac/antomop/qris.md) | Payment discrepancy may occur. | When such payment discrepancies occur (the user makes a payment but you receive a notification of payment failure), once Antom receives a payment success message from the downstream payment method, Antom will settle the funds to you. After you receive the funds, you can independently handle any subsequent refunds. And the corresponding settlement details report will include this transaction. |
| [PromptPay](https://docs.antom.com/ac/antomop/promptpay.md) | Payment discrepancy may occur. |  |
| [Konbini](https://docs.antom.com/ac/antomop/konbini_mdx.md) ,  [Konbini (7-Eleven)](https://docs.antom.com/ac/antomop/Konbini_711_mdx.md) ,  [Pay-easy](https://docs.antom.com/ac/antomop/payeasy.md) | - Cannot redirect back to the merchant page. - Payment discrepancy may occur. |  |
| Maybank | Once the payment is completed, there may be a 5-minute delay in sending the asynchronous notification. | Display a prompt of "order is being confirmed" to buyers. |
| [OVO](https://docs.antom.com/ac/antomop/ovo.md) ,  [Pix](https://docs.antom.com/ac/antomop/pix.md) ,  [BANCOMAT Pay](https://docs.antom.com/ac/antomop/bancomatpay.md) | The payment method app cannot be launched automatically. | The buyer must manually launch the payment method app to complete the payment. |
| [Siam Commercial Bank](https://docs.antom.com/ac/antomop/scb.md) | - A processing fee may be applied to payments. - Not supported on PC. | Prompt the buyer that a processing fee may be applied to payments made using the payment method. The fee amount is determined by the buyer's bank level. |
| [Bank of Ayudhya](https://docs.antom.com/ac/antomop/bay.md) | - A processing fee may be applied to payments. - Not supported on PC. | Prompt the buyer that a processing fee may be applied to payments made using the payment method. The fee amount is determined by the buyer's bank level. |
| [GoPay](https://docs.antom.com/ac/antomop/gopay.md) ,  [Bangkok Bank](https://docs.antom.com/ac/antomop/bangkok_bank.md) ,  [Siam Commercial Bank](https://docs.antom.com/ac/antomop/scb.md) ,  [Bank of Ayudhya](https://docs.antom.com/ac/antomop/bay.md) ,  [KrungThai Bank](https://docs.antom.com/ac/antomop/kyb.md) | Not supported on PC. | None |
| [NAVER Pay](https://docs.antom.com/ac/antomop/naverpay.md) | - Requires additional integration for PC clients. - After login, the buyer completes the payment in a new tab instead of the original page. | Enable browser pop-up permissions and use a new     *browser*     object or window to handle the payment. |
| [Express Bank Transfer](https://docs.antom.com/ac/antomop/express_bank_transfer.md) | *paymentRedirectUrl*     has restrictions on special characters. | Avoid using the "&" symbol in     *paymentRedirectUrl*    . |

## Card payment features {#abqU1}

 Payment Element supports the following card payment features.    Click to get    detailed information and usage instructions:

 - [3D Secure 2](https://docs.antom.com/ac/pm/3ds.md)
- [Antom 3DS-Retry](https://docs.antom.com/ac/pm/3ds_retry.md)
- [MIT (Merchant-Initiated Transaction)](https://docs.antom.com/ac/pm/mit.md)
- [Antom Tokenization](https://docs.antom.com/ac/pm/tokenization.md)
- [COF (Card-on-File)](https://docs.antom.com/ac/pm/cof.md)

# Additional content {#GBkuL}

 Antom also offers the following customization options:

 - [**Appearance customization**](https://docs.antom.com/ac/cashierpay/appearance.md)  : Antom provides extensive styling options, including theme, layout, and CSS customization.
- [**Google Pay**](https://docs.antom.com/ac/subscriptionpay/gpay_element_subscription.md)  : Buyers can pay using credit or debit cards stored in their Google account. With Payment Element, no additional [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md) SDK integration is needed—it loads [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md) automatically.
- [**Apple Pay**](https://docs.antom.com/ac/subscriptionpay/apay_element_subscription.md)  : Buyers can pay using credit or debit cards stored in their Apple account. With Payment Element, no additional [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) SDK integration is needed—it loads [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md) automatically.

## Specify a payment method {#L9xOX}

 You can pass the parameters in the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API to specify the display of payment methods on Payment Element, the order of the payment method list, and the display of quick payments. This feature offers you the following benefits:

 - Filter local payment methods based on your business region.
- Sort your preferred payment methods.
- Display the mainstream quick payments, such as [Alipay](https://docs.antom.com/ac/antomop/alipay_cn.md), [Apple Pay](https://docs.antom.com/ac/antomop/applepay.md), and [Google Pay](https://docs.antom.com/ac/antomop/googlepay.md).

 To specify the payment method, pass the following parameters in the     *availablePaymentMethod*     parameter of the   [**createPaymentSession (One-time Payments)**](https://docs.antom.com/ac/ams/session_cashier.md)   API:

 | **Parameter name** | **Required** | **Description** |
| --- | --- | --- |
| *paymentMethodTypeList* | No | List of payment methods available to the buyer. The list includes the following parameters: - *paymentMethodType*    : The type of payment method included in the payment option. See   [Enumeration values of payment methods](https://docs.antom.com/ac/pm/enumeration_values.md)   for valid values. - *paymentMethodOrder*    : The priority order you set for payment methods, represented by sequence numbers. The smaller the number, the higher the priority. If no sequence is set, Antom's default sorting will be used. - *expressCheckout*    : Specifies whether to enable the express checkout option for a payment method. Currently, only   `ALIPAY_CN`  ,   `APPLEPAY`   and   `GOOGLEPAY`   can be set as express checkout methods. Valid values are:     - `true`  :    Enables express checkout for this payment method.   - `false`  : Disables express checkout for this payment method.   > **[INFO]** **Note**    : The     *expressCheckout*     parameter is currently supported only on Android and iOS. |

 The following is a sample code for specifying the payment method:

 ```json
{ ...
  "availablePaymentMethod": {
    "paymentMethodTypeList": [
      {
        "paymentMethodType": "ALIPAY_CN",
        "paymentMethodOrder": "0"
      },
      {
        "paymentMethodType": "TNG",
        "paymentMethodOrder": "1"
      }
    ]
  }
}
```

## Payment methods requiring embedding {#Ygtd4X}

 The following payment methods require you to embed the payment details collection component:

 | **Payment method category** | **Payment method** | **Enum value** |
| --- | --- | --- |
| Card | [Mastercard](https://docs.antom.com/ac/antomop/mastercard_mdx.md) | `CARD` |
| [Visa](https://docs.antom.com/ac/antomop/visa_mdx.md) | `CARD` |  |
| [American Express](https://docs.antom.com/ac/antomop/amex_mdx.md) | `CARD` |  |
| [Brazilian cards](https://docs.antom.com/ac/antomop/brazilian_mdx.md) | `CARD` |  |
| [Chilean cards](https://docs.antom.com/ac/antomop/chilean_mdx.md) | `CARD` |  |
| [Diners](https://docs.antom.com/ac/antomop/diners_mdx.md) | `CARD` |  |
| [Discover](https://docs.antom.com/ac/antomop/discover_mdx.md) | `CARD` |  |
| [South Korean Cards (Local cards)](https://docs.antom.com/ac/antomop/south_korean_mdx.md) | `CARD` |  |
| [Mexican cards](https://docs.antom.com/ac/antomop/mexican_mdx.md) | `CARD` |  |
| [Peruvian cards](https://docs.antom.com/ac/antomop/peruvian_mdx.md) | `CARD` |  |
| [UnionPay](https://docs.antom.com/ac/antomop/unionpay_mdx.md) | `CARD` |  |
| Digital wallet | [OVO](https://docs.antom.com/ac/antomop/ovo.md) | `OVO` |
| [Mercado Pago](https://docs.antom.com/ac/antomop/mp_mdx.md)   (Mexico) | `MERCADOPAGO_MX` |  |
| [Mercado Pago](https://docs.antom.com/ac/antomop/mp_mdx.md)   (Brazil) | `MERCADOPAGO_BR` |  |
| [Mercado Pago](https://docs.antom.com/ac/antomop/mp_mdx.md)   (Peru) | `MERCADOPAGO_PE` |  |
| [Mercado Pago](https://docs.antom.com/ac/antomop/mp_mdx.md)   (Chile) | `MERCADOPAGO_CL` |  |
| Cash payment | [Konbini (7-Eleven)](https://docs.antom.com/ac/antomop/Konbini_711_mdx.md) | `SEVENELEVEN` |
| [Konbini](https://docs.antom.com/ac/antomop/konbini_mdx.md) | `KONBINI` |  |
| Online banking | [BANCOMAT Pay](https://docs.antom.com/ac/antomop/bancomatpay.md) | `BANCOMATPAY` |
| [BLIK](https://docs.antom.com/ac/antomop/blik.md) | `BLIK` |  |
| [Pay-easy](https://docs.antom.com/ac/antomop/payeasy.md) | `ONLINEBANKING_PAYEASY` |  |
| [FPX](https://docs.antom.com/ac/antomop/fpx.md) | `ONLINEBANKING_FPX` |  |