# Server-to-server mode

> Learn how to integrate card payment methods through the server-to-server mode using API.

The One-time Payments service offers comprehensive payment solutions for websites and applications, enabling seamless integration across desktop and mobile platforms. With a one-time integration, businesses can quickly access multiple online payment methods, including e-wallets, bank transfers, and card payments, effectively reducing technical barriers. Buyers are empowered to choose the most convenient and secure payment option according to their personal preferences, ensuring a superior payment experience.

This article describes how to integrate card payment methods using the server-to-server mode. This integration approach is suitable for merchants who require a high level of customization in their payment processes. The server-to-server integration requires PCI compliance; please confirm the specific PCI qualification with the respective acquirers.

The following are the PCI qualification requirements for Antom:

-   If your annual card transaction volume is expected to exceed 6 million, complete and submit the [PCI Attestation of Compliance (AoC)](https://docs-prv.pcisecuritystandards.org/PCI%20DSS/Reporting%20Template%20or%20Form/PCI-DSS-v4-0-ROC-AOC-Merchants-r1.pdf) file for verification.
-   If your annual card transaction volume is expected to be below 6 million, complete and submit the [PCI DSS Self-Assessment Questionnaires (SAQs)](https://docs-prv.pcisecuritystandards.org/SAQ%20(Assessment)/SAQ/PCI-DSS-v4-0-SAQ-D-Merchant-r1.pdf) file for verification.

For more information about PCI DSS compliance requirements, see [PCI DSS standard](https://www.pcisecuritystandards.org/).

> **Note**: The hosted mode integration for card payments calls the same set of APIs as the server-to-server mode. If you do not have PCI qualification, or if you do not wish to collect card information yourself, opt for the [Hosted mode](https://docs.antom.com/ac/apo/cardcollant.md) integration.

## User experience

### First payment

#### Tab: Web

#### Web user experience

![web-首次支付.png](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2025/png/1b235d5a-c942-4311-874b-6ed2819136c5.png)

#### Tab: App

#### App user experience

![App-首次支付.png](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2025/png/7cbd21ee-41b9-416e-8d03-3f74591fe0bf.png)

### Saved card payments

#### Tab: Web

#### Web user experience

![web-已存卡支付.png](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2025/png/7974ecf4-212c-4c1b-acce-c525884b248b.png)

#### Tab: App

#### App user experience

![App-已存卡支付.png](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2025/png/34dd962a-225d-4dfb-b537-13fcb7608436.png)

## Payment flow

The payment flow of the server-to-server card payment integration is composed of the following steps:

![Server-to-server 流程图英文.png](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2025/png/fd6c73cd-f36b-4d24-bfd2-57c365977f7a.png)

1.  **The buyer enters the card information and select whether to save the card.**
2.  **Create a payment request.**
    After the buyer selects a payment method and submits the order, the merchant server calls the [**pay**](https://docs.antom.com/ac/apo/pay.md) API to obtain the payment link to complete the payment.
3.  **Redirect to the 3D authentication page.**
    The buyer is redirected to the 3D authentication page to complete identity verification (if frictionless authentication is triggered, verification is not required), and returns to the merchant result page.
4.  **Obtain the authorized payment result.**
    The merchant server receives the payment result notification returned by the payment method, and processes the order based on the returned result.
5.  **Capture and obtain the capture result.**
    You must intiate capture after the authorized payment is successful. You can obtain the capture result using one of the following methods:

-   Asynchronous notification: Specify _paymentNotifyUrl_ in the [**pay**](https://docs.antom.com/ac/apo/pay.md) API to set the address for receiving asynchronous notifications. When the payment request is successful or expires, APO uses the [**notifyCapture**](https://docs.antom.com/ac/apo/notify_capture.md) API to send asynchronous notifications to you.
-   Synchronous inquiry: Call the [**inquiryPayment**](https://docs.antom.com/ac/apo/paymentri_online.md) API to check the payment request status.

> **Note:** If you have activated the same payment method with multiple acquirers, you can configure the payment routing rule on [APO Dashboard](https://dashboard.alipay.com/global-payments/developers/iNotify). If no payment routing rules are set, APO will use random routing rules.

# Integration preparations

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

-   Obtain a client ID and an agent token (applicable to ISV model)
-   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).

## Integration steps

Start your integration by taking the following steps.

1.  (Optional) Display the payment methods
2.  Authorize a payment
3.  (Optional) Redirect to 3D authorization URL
4.  Obtain the authorized payment result
5.  Capture and obtain the capture result

### Step 1: (Optional) Display payment methods Client-side

#### Tab: First payment

#### First-time payments

Add the logos and names of the payment methods you plan to integrate. This allows buyers to easily choose their preferred method of payment. You can contact APO Technical Support to source the logos and names.

The following figure visualizes the payment method list:

![5.png](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2025/png/6c017402-0e68-4852-8ab3-47a2f095d020.png)

#### Tab: Saved card payments

#### Saved card payments

You can display the card brand name, logo, and masked card number for buyers to select and proceed with saved card payments.

![6.png](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2025/png/e674ca53-ae33-4ab8-ad9f-ca3dc7956dfd.png)

### Step 2: Authorize a payment Server-side

When the buyer selects a payment method, you need to collect the key order information, such as the buyer's payment method information, order information, device information, and payment amount. Call the [**pay**](https://docs.antom.com/ac/apo/pay.md) API, and submit an authorization request for payment.

Initiating a payment request involves the following key parameters:

| **Parameter name** | **Whether** **required or not** | **Description** |
| --- | --- | --- |
| _productCode_ | Yes | The value of this parameter in this scenario is fixed as `CASHIER_PAYMENT`. |
| _paymentRequestId_ | Yes | The request ID of the authorized payment, which must be unique each time. |
| _paymentAmount_ | Yes | The payment amount. The amount to charge is a positive integer in the smallest currency unit, e.g., CNY in fen and KRW in won. |
| _paymentMethod_ | Yes | The enum value of payment methods. The value of this parameter in this scenario is fixed as `CARD`. |
| _paymentRedirectUrl_ | Yes | The merchant payment result page URL that the buyer is redirected to after the payment is completed. The display of this page is based on server-side results, and must not be fixed as a payment successful page. |
| _paymentNotifyUrl_ | No | The URL that is used to receive the payment result notification. You can set the URL to receive the notification via the API or through [APO Dashboard](https://dashboard.alipay.com/global-payments/account/login?goto=https%3A%2F%2Fdashboard.alipay.com%2Fglobal-payments%2Fhome). |
| _settlementStrategy_ | No | The settlement strategy for the payment request. Specify the _settlementCurrency_ parameter in the API if you signed up for multiple settlement currencies. This parameter is only applicable when the acquirer is Antom. |
| _order.buyer_ | Yes | The buyer information of the merchant side. At least one of the following information must be provided: - _order.buyer.referenceBuyerId_ - _order.buyer.buyerPhoneNo_ - _order.buyer.buyerEmail_ |
| _order.referenceOrderId_ | Yes | The order ID of the merchant side. |
| _order.orderDescription_ | Yes | The order description of the merchant side. |
| _env.terminalType_ | Yes | The terminal type from which the buyer initiates the payment. Valid values are: - `WEB`: Merchant's PC website. - `WAP`: Merchant's H5 website. - `APP`: Merchant's mobile application (app). |
| _env.osType_ | No | The environment where the buyer initiates a payment. When the buyer initiates on a merchant's mobile browser website, the value of this parameter is `ANDROID` or `IOS`. |
| _env.clientIp_ | Yes | The buyer's current IP. In the card payment scenario, the buyer's IP information must be provided. |
| _paymentFactor.isAuthorization_ | Yes | The payment mode. This parameter must be set as `true`, which indicates that the payment scenario is authorization. |
| _paymentMethod.paymentMethodMetaData.cardNo_ | Yes | In the card payment scenario, you must collect the card information and pass in the plaintext card number. |
| _paymentMethod.paymentMethodMetaData.expiryYear_ | Yes | In the card payment scenario, you must collect the card information and pass in the card expiration year. |
| _paymentMethod.paymentMethodMetaData.expiryMonth_ | Yes | In the card payment scenario, you must collect the card information and pass in the card expiration month. |
| _paymentMethod.paymentMethodMetaData.cvv_ | No | In the card payment scenario, during the first payment or when using a new card for subsepuent payments, providing the card's CVV information can significantly enhance the success rate of the payment. |
| _paymentMethod.paymentMethodMetaData.cardholderName_ | Yes | In card payment scenarios, providing the cardholder's name can enhance the success rate of the payment. This parameter only supports English characters. |
| _paymentMethod.paymentMethodMetaData.is3DSAuthentication_ | No | Indicates whether the transaction authentication type is 3D Secure. Merchants should determine whether to require 3D authentication for an order based on the current risk and dispute considerations. Valid values are: - `true`: APO passes the transaction as 3D-secured to the corresponding acquirers. - `false`: If the parameter is not configured or set to `false`, APO passes the transaction as non-3D-secured to the corresponding acquirers. However, the final processing decision depends on the acquirer. |

For more information about the full parameters and other requirements of specific payment methods, refer to the **[pay](https://docs.antom.com/ac/apo/pay.md)** API.

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

```java
public static void payByCardServer2Server() {
    AlipayPayRequest alipayPayRequest = new AlipayPayRequest();
    alipayPayRequest.setProductCode(ProductCodeType.CASHIER_PAYMENT);

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

    // set amount
    Amount amount = Amount.builder().currency("SGD").value("4200").build();
    alipayPayRequest.setPaymentAmount(amount);

    // set payment methods
    PaymentMethod paymentMethod = PaymentMethod.builder().paymentMethodType("CARD").build();
    alipayPayRequest.setPaymentMethod(paymentMethod);

    // card info
    Map<String, Object> paymentMethodMetaData = new HashMap<String, Object>();
     paymentMethodMetaData.put("tokenizeMode", ASKFORCONSENT);
     paymentMethod.setPaymentMethodMetaData(paymentMethodMetaData);

    // replace with your orderId
    String orderId = UUID.randomUUID().toString();

    // set buyer info
    Buyer buyer = Buyer.builder().referenceBuyerId("yourBuyerId").build();

    // set order info
    Order order = Order.builder().referenceOrderId(orderId)
        .orderDescription("antom testing order").orderAmount(amount).buyer(buyer).build();
    alipayPayRequest.setOrder(order);

    // set environment info
    Env env = Env.builder().terminalType(TerminalType.WEB).clientIp("1.2.3.4").build();
    alipayPayRequest.setEnv(env);

    // set authorization capture payment mode
    PaymentFactor paymentFactor = PaymentFactor.builder().isAuthorization(true).build();
    alipayPayRequest.setPaymentFactor(paymentFactor);

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

    // replace with your redirect url
    alipayPayRequest.setPaymentRedirectUrl("https://www.yourMerchantWeb.com");

    // process payment
    AlipayPayResponse alipayPayResponse = null;
    try {
        alipayPayResponse = CLIENT.execute(alipayPayRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // handle error condition
    }
}
```

The following shows the sample code of a payment request:

```json
{
  "env":{
    "browserInfo":{
      "acceptHeader":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
      "javaEnabled":false,
      "javaScriptEnabled":false,
      "language":"en",
      "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
    },
    "clientIp":"172.*.*.*",
    "colorDepth":24,
    "deviceId":"d0e9ee6d-be3a-487d-b514-69495000be72",
    "deviceLanguage":"en",
    "screenHeight":723,
    "screenWidth":1536,
    "terminalType":"WEB",
    "timeZoneOffset":0
  },
  "order":{
    "buyer":{
      "buyerEmail":"ken***@gmail.com",
      "buyerName":{
        "firstName":"*******",
        "lastName":"schmitt "
      },
      "buyerPhoneNo":"7046518433",
      "buyerRegistrationTime":"2022-01-10T20:33:07+08:00",
      "referenceBuyerId":"2095667"
    },
    "goods":[
      {
        "deliveryMethodType":"PHYSICAL",
        "goodsCategory":"Bikini Sets",
        "goodsName":"V-neck Strawberry Knotted Tankini Cheeky Bikini Set",
        "goodsQuantity":"1",
        "goodsUnitAmount":{
          "currency":"USD",
          "value":"2290"
        },
        "goodsUrl":"https://m.shopcider.com/product/detail?pid=114496585",
        "referenceGoodsId":"114496585"
      },
      {
        "deliveryMethodType":"PHYSICAL",
        "goodsCategory":"3 Piece Bikini Sets",
        "goodsName":"V-neck Floral Halter Cheeky Bikini Swimsuit With Floral Cover Up Skirt",
        "goodsQuantity":"1",
        "goodsUnitAmount":{
          "currency":"USD",
          "value":"2790"
        },
        "goodsUrl":"https://m.shopcider.com/product/detail?pid=111038664",
        "referenceGoodsId":"111038664"
      },
      {
        "deliveryMethodType":"PHYSICAL",
        "goodsCategory":"3 Piece Bikini Sets",
        "goodsName":"V-neck Halter Floral Tie Side Bikini Swimsuit With Sarong",
        "goodsQuantity":"1",
        "goodsUnitAmount":{
          "currency":"USD",
          "value":"2790"
        },
        "goodsUrl":"https://m.shopcider.com/product/detail?pid=107371032",
        "referenceGoodsId":"107371032"
      },
      {
        "deliveryMethodType":"PHYSICAL",
        "goodsCategory":"One-pieces ",
        "goodsName":"Shaping Lettuce Trim Cut Out Shell Chain O-Ring One Piece Swimsuit",
        "goodsQuantity":"1",
        "goodsUnitAmount":{
          "currency":"USD",
          "value":"2490"
        },
        "goodsUrl":"https://m.shopcider.com/product/detail?pid=114267374",
        "referenceGoodsId":"114267374"
      },
      {
        "deliveryMethodType":"PHYSICAL",
        "goodsCategory":"One-pieces ",
        "goodsName":"Shaping Halter Floral Bowknot Ruched One Piece Swimsuit",
        "goodsQuantity":"1",
        "goodsUnitAmount":{
          "currency":"USD",
          "value":"1990"
        },
        "goodsUrl":"https://m.shopcider.com/product/detail?pid=107966234",
        "referenceGoodsId":"107966234"
      }],
    "orderAmount":{
      "currency":"USD",
      "value":"13184"
    },
    "orderDescription":"Shop in Cider.",
    "referenceOrderId":"8203426867",
    "shipping":{
      "shipToEmail":"ken***@gmail.com",
      "shippingAddress":{
        "address1":"*************************",
        "city":"Waxhaw",
        "region":"US",
        "state":"NC",
        "zipCode":"28173"
      },
      "shippingName":{
        "firstName":"*******",
        "lastName":"schmitt "
      },
      "shippingPhoneNo":"7046518433"
    }
  },
  "paymentAmount":{
    "currency":"USD",
    "value":"13184"
  },
  "paymentFactor":{
    "captureMode":"MANUAL",
    "isAuthorization":true
  },
  "paymentMethod":{
    "paymentMethodMetaData":{
      "billingAddress":{
        "address1":"*******************",
        "address2":"",
        "city":"waxhaw",
        "region":"US",
        "state":"NC",
        "zipCode":"28173"
      },
      "cardNo":"************8842",
      "cardholderName":{
        "firstName":"*******",
        "lastName":"schmitt "
      },
      "cvv":"***",
      "expiryMonth":"**",
      "expiryYear":"****"
    },
    "paymentMethodType":"CARD"
  },
  "paymentNotifyUrl":"https://pay.shopcider.com/pay/v2/notify/callback?platform=ALIPAY",
  "paymentRedirectUrl":"https://www.shopcider.com/payment/success?tradeNo=20250515141372578176868806656&oid=8203426867&payType=ALIPAY_CARD_DIRECT_US&ovt=e51e7e02c94340e7809f5b9a3768ffe4",
  "paymentRequestId":"20250515141372578176868806656",
  "productCode":"CASHIER_PAYMENT",
  "settlementStrategy":{
    "settlementCurrency":"USD"
  }
}
```

The following shows the sample code of a response, which contains the following key parameters:

-   _acquirerInfo_: The information of the acquirer. It is recommended to save the information of the acquirer (_acquirerInfo_), refer to [Order number management](https://docs.antom.com/ac/apo_zh-cn/iddemon.md) for specific reasons.
-   _result.resultStatus_: The status of the authorized payment.
-   _normalUrl_: The URL used to redirect to the 3D authentication page.

#### Tab: Successful authorized payments

```json
{
  "acquirerInfo":{
    "acquirerMerchantId":"2181110021840423",
    "acquirerName":"ALIPAY",
    "acquirerResultCode":"SUCCESS",
    "acquirerResultMessage":"success",
    "acquirerTransactionId":"20250514114010800100181020233302045",
    "referenceRequestId":"20250514141372220148107038720"
  },
  "authExpiryTime":"2025-05-13T23:33:01-07:00",
  "paymentAmount":{
    "currency":"USD",
    "value":"1339"
  },
  "paymentCreateTime":"2025-05-13T23:32:59-07:00",
  "paymentId":"20250514114010800100181020233302045",
  "paymentRequestId":"20250514141372220148107038720",
  "paymentResultInfo":{
    "avsResultRaw":"Y",
    "cardBrand":"VISA",
    "cardNo":"************9484",
    "cvvResultRaw":"Y",
    "networkTransactionId":"385134235816498",
    "threeDSResult":{
      "cavv":"AQAAAAAAPTYgKe8AmdDhgoYAAAA=",
      "eci":"07"
    }
  },
  "paymentTime":"2025-05-13T23:33:01-07:00",
  "result":{
    "resultCode":"SUCCESS",
    "resultMessage":"success.",
    "resultStatus":"S"
  }
}
```

#### Tab: Failed authorized payments

```json
{
  "result": {
    "resultCode": "ACCESS_DENIED",
    "resultMessage": "Access is denied.",
    "resultStatus": "F"
  }
}
```

#### Tab: Authorized payment in process

```json
{
    "authExpiryTime": "2025-02-27T15:15:57+08:00",
    "normalUrl": "https://ac.alipay.com/page/sandbox/stone-infostaging.html?paymentId=20250220194010800190188000014991264&code=golcashier99ed8d93-efcf-4fe7-b9a1-4bb1152d6ad4sandbox&golSandbox=true&pspName=CARD&county=SG",
    "paymentActionForm": "{\"method\":\"GET\",\"paymentActionFormType\":\"RedirectActionForm\",\"redirectUrl\":\"https://ac.alipay.com/page/sandbox/stone-infostaging.html?paymentId=20250220194010800190188000014991264&code=golcashier99ed8d93-efcf-4fe7-b9a1-4bb1152d6ad4sandbox&golSandbox=true&pspName=CARD&county=SG\"}",
    "paymentAmount": {
        "currency": "HKD",
        "value": "30000"
    },
    "paymentCreateTime": "2025-02-20T15:15:57+08:00",
    "paymentId": "20250220194010800190188000014991264",
    "paymentRequestId": "13eIlShnRH5mKYtqFa2kvfht2IXdx1XoOg0S9jd3pMWzg3iOBwoXCv1YuEUyno0A",
    "redirectActionForm": {
        "method": "GET",
        "redirectUrl": "https://ac.alipay.com/page/sandbox/stone-infostaging.html?paymentId=20250220194010800190188000014991264&code=golcashier99ed8d93-efcf-4fe7-b9a1-4bb1152d6ad4sandbox&golSandbox=true&pspName=CARD&county=SG"
    },
    "result": {
        "resultCode": "PAYMENT_IN_PROCESS",
        "resultMessage": "Payment is processing.",
        "resultStatus": "U"
    }
}
```

The table shows the possible values that the _result.resultStatus_ parameter in the response message may return. Please handle the result according to the guidances:

| **_result.resultStatus_** | **Message** | **Further actions** |
| --- | --- | --- |
| `S` | Indicates that the authorized payment is successful. | You may store the _paymentId_ information, and proceed with initiating capture. |
| `F` | Indicates the authorized payment failed. | Please close the current transaction or retry using a new _paymentRequestId_. |
| `U` | Indicates that the authorized payment is being processed. | - If _normalUrl_ is returned, the merchant frontend should redirect to _normalUrl_. - If _normalUrl_ is not returned, it indicates that the transaction is being processed. You can call the **[inquiryPayment](https://docs.antom.com/ac/apo/paymentri_online.md)** API to retrieve the payment result, or wait for the asynchronous payment result notifications. |

> **Note**: If you did not receive a response message, it might be due to a network timeout. You can call the **[inquiryPayment](https://docs.antom.com/ac/apo/paymentri_online.md)** API to retrieve the payment result, or wait for the asynchronous payment result notifications.
>
> **Common Questions**
>
> **Q: What is** **_paymentId_** **？**
>
> A: If you need to store the corresponding order number for future refunds and reconciliation, you can specify _paymentId_. 
>
> **Q: What is the information in** _**acquirerInfo**_ **and how do I use it**_**?**_
>
> A: This parameter indicates the information about the acquirer, refer to [Order number management](https://docs.antom.com/ac/apo_zh-cn/iddemon.md) for more information.
>
> **Q: How to configure** **_terminalType_****？**
>
> A: The valid values of _terminalType_ 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. Additionally, fill in the corresponding _env.osType_ parameter as `ANDROID` or `IOS` based on the buyer's mobile device. 
> -   `APP`: The client-side terminal type is a mobile application.
>
> **Q: How to identify if a transaction requires 3D authentication？**
>
> A: You can decide whether the transaction requires 3D authentication based on the current risk and dispute considerations. Valid values for configuring _is3DSAuthentication_ are:
>
> -   `true`: APO passes the transaction as 3D-secured to the corresponding  acquirers.
> -   `false`: If the parameter is not configured or set to `false`, APO passes the transaction as non-3D-secured to the corresponding acquirers. However, the final processing decision depends on the acquirer.
>
> **Q: In the case where a transaction does not require 3D authentication, will the payment result of the authorized payment be returned directly?**
> A: No. The returned authorized payment results may include the following situations:
>
> -   The payment is being processed:
>
> -   If the redirect URL is not returned, you need to wait for the asynchronous payment result notifications or call the **[inquiryPayment](https://docs.antom.com/ac/apo/paymentri_online.md)** API to retrieve the payment result. You must not mark it as a payment failure.
> -   If the redirect URL is returned, you need to redirect the buyer to the URL.
>
> -   No response: It might be due to a network timeout. You can call the **[inquiryPayment](https://docs.antom.com/ac/apo/paymentri_online.md)** API to retrieve the payment result, or wait for the asynchronous payment result notifications. You must not mark it as a payment failure.
> -   The payment is successful: You can proceed with initiating capture.
> -   The payment failed: Guide the buyer to try again.

### Step 3: (Optional) Redirect to the 3D authentication page Client-side

Once the merchant’s server obtains the _normalUrl_ returned by APO, it should pass _normalUrl_ to the frontend, which will then redirect the buyer to the 3D authentication page.

> **Note**: This step is not applicable if the [**pay**](https://docs.antom.com/ac/apo/pay.md) API returns _result.resultStatus_ with a value of `S` or `F`, as the authorized payment stage has reached its final state.

#### Tab: WEB

After obtaining _normalUrl,_ you need to redirect the page to the 3D authentication page in the browser, or open it in a new tab.

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

#### Tab: WAP

After obtaining _normalUrl,_ you need to redirect the page to the 3D authentication page in the browser, or open it in a new tab.

```javascript
window.location.href = URL;
```

#### Tab: iOS

After receiving _normalUrl_ from APO, the merchant app redirects to the system default browser to load the 3D authentication page. The buyer submits payments on this page.

```objectivec
// Initialize webview configuration
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
// Initialize webView
WKWebView *webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) configuration:configuration];
webView.navigationDelegate = self;
//Load the payment method URL
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:Url]]];
[self.view addSubview:self.webView];
```

#### Tab: Android

After receiving _normalUrl_ from APO, the merchant app redirects to the system default browser to load the 3D authentication page. The buyer submits payments on this page.

```java
WebView webView = findViewById(R.id.webview);
//Set webview
webView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        view.loadUrl(request.getUrl().toString());
        return super.shouldOverrideUrlLoading(view, request);
    }});

WebSettings webSettings = webView.getSettings();
//Enable javascript
webSettings.setJavaScriptEnabled(true);
//Enable scaling
webSettings.setSupportZoom(true);
//Enable scaling controls (buttons)
webSettings.setBuiltInZoomControls(true);
//Webview has two cache modes. Cache is not used here.
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
//Allow JavaScript to open a new tab (false by default)
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
//Allow JavaScript to load local storage
webSettings.setDomStorageEnabled(true);
//WAP cache size (settings are not needed)
//webSettings.setAppCacheMaxSize(1024 * 1024 * 8);
//WAP cache path
String absolutePath = getApplicationContext().getCacheDir().getAbsolutePath();
//WAP cache size
webSettings.setAppCachePath(absolutePath);
//Set whether to allow WebView to access files (true by default)
webSettings.setAllowFileAccess(true);
//Allow WAP cache to be saved
webSettings.setAppCacheEnabled(true);
//In preview mode, if the page width exceeds the WebView display, scale down the page to fit WebView (false by default)
webSettings.setLoadWithOverviewMode(true);
//Support the viewport HTML meta tag
webSettings.setUseWideViewPort(true);
//Load the payment method URL
webView.loadUrl(Url);
```

Different terminal returns different _normalUrl_. APO will decide which _normalUrl_ to return based on the _paymentMethod_ and _terminalType_ information provided by you.

#### Tab: Web

![3d web.png](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2025/png/31332036-16fd-4800-9ffe-f135158414b6.png)

#### Tab: App

![3d app.png](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/yuque/idocs/2025/png/9c4187a9-8d07-4aad-b127-712b42553b4e.png)

> **Common Questions**
>
> **Q: How to handle the returned 3D** **authentication** **page URL?**
>
> A: For web (Web/WAP), you can directly process the redirection; For mobile, you can open the page in a web browser; however, please note that in some cases, specific card issuers may require their own banking app to be launched, which cannot be universally guaranteed in all integration scenarios.
>
> **Q:** **How should the content of the payment result page be displayed?**
>
> A:
>
> -   Both successful and failed payments may provide an option to redirect from the payment method back to the merchant’s page. Therefore, do not fix _paymentRedirectUrl_ as the payment success page; instead, base it on the result returned by your server to prevent confusion for buyers.
> -   If the transaction is initiated from a mobile application, you need to set _paymentRedirectUrl_ as the scheme address of the merchant application.
>
> **Q:** **Does the redirection to the merchant result page indicate a successful payment?**
>
> A: No. You cannot determine payment success solely based on the redirection to the merchant page, and APO will not append any payment result parameters indicating the payment result to _paymentRedirectUrl_. Redirection to the merchant result page may occur in the following situations:
>
> -   After the buyer successfully completes a payment, they may not be redirected to the merchant’s page due to network or other issues.
> -   Even if the buyer has not completed the payment, they may also return to the merchant page through the entry point provided by the payment method.

### Step 4: Obtain the authorized payment result Server-side

#### 1\. Set the webhook URL to receive notifications

You can choose one of following methods to set the webhook URL to receive notifications:

-   If each of your order has a unique notification URL, we recommend to set the webhook URL in each individual request. You can pass the asynchronous notification receiving URL for the specific order through the _paymentNotifyUrl_ parameter in the [**pay**](https://docs.antom.com/docs/ac/apo/pay.md) API.
-   If all your orders share a unified notification URL, you can set the webhook URL on [APO Dashboard](https://dashboard.alipay.com/global-payments/developers/iNotify) through **Developer > Notification Address**.

> **Note**:
>
> -   If you set the webhook URL through both the API and APO Dashboard, the one set in the [**pay**](https://docs.antom.com/docs/ac/apo/pay.md) API takes precedence.
> -   If the buyer fails to verify their identity after being redirected to the 3D authentication page, APO will only send an asynchronous notification after the order is closed. The default payment tiemout depends on the acquirer.

The following is the notification request sample code:

```json
{
  "acquirerInfo":{
    "acquirerMerchantId":"WF_wallet_UK",
    "acquirerName":"ADYEN",
    "acquirerTransactionId":"PK6K679WDZS2K8F3",
    "referenceRequestId":"N2025021716031300000460202933868"
  },
  "acquirerReferenceNo":"PK6K679WDZS2K8F3",
  },
  "notifyType":"PAYMENT_RESULT",
  "paymentAmount":{
    "currency":"GBP",
    "value":"1523"
  },
  "paymentCreateTime":"2025-02-16T21:48:05-08:00",
  "paymentId":"20250217164010890100111460207325722",
  "paymentRequestId":"2025021786031300001258802935146",
  "paymentResultInfo":{
    "avsResultRaw":"Y",
    "cardBin":"439654",
    "cardBrand":"VISA",
    "cardNo":"************6044",
    "cvvResultRaw":"M",
    "fingerprint":"2a1b042f23d1a0927175e4fb8893ab9be5f0ffeff54ed87b88f433671fa67cf4",
    "funding":"DEBIT",
    "issuerName":"WISE PAYMENTS LIMITED",
    "issuingCountry":"GB",
    "threeDSResult":{
      "cavv":"N/A",
      "eci":"N/A",
      "threeDSType":"INTERNAL",
      "xid":"N/A"
    }
  },
  "paymentTime":"2025-02-16T21:48:06-08:00",
  "result":{
    "resultCode":"SUCCESS",
    "resultMessage":"success.",
    "resultStatus":"S"
  }
}
```

The table shows the possible values that the _result.resultStatus_ parameter in the request message may return. Please handle the result according to the guidances:

| **_result.resultStatus_** | **Message** | **Further actions** |
| --- | --- | --- |
| `S` | Indicates that the authorized payment is successful. | You can store the _p__aymentId_ information, and proceed with initiating capture. |
| `F` | Indicates the authorized payment failed. | Please close the current transaction or replace _paymentRequestId_ to place the order again. > **Note**: In certain failure scenarios, for example, if there are parameter exceptions, the **[pay](https://docs.antom.com/ac/apo/pay.md)** API will synchronously return `F` and will not send any asynchronous notification. You can directly close the order based on the `F` status. |

#### 2\. Verify asynchronous notifications

If you receive an asynchronous notification from APO, you are required to return the response in the [Sample code](https://docs.antom.com/ac/apo/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 APO.

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

}
```

You do not need to countersign the response of the result notification. However, you must respond to each notification request in the following fixed format, regardless of whether the payment is successful or not.

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

> **Common Questions**
>
> **Q: When will the notification be sent?**
>
> A: The sending time depends on whether the payment is completed. If the payment is completed, APO will send an asynchronous notification immediately after receiving notification from the corresponding acquirer.
>
> **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 APO, but you didn't make a response to the notification in the [Sample code](https://docs.antom.com/ac/apo/notifications.md#wkNv0) format.
>
> The notification can be re-sent 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:** **Can the shipping be based on** **_resultStatus_** **with the value** **`S`****?** 
>
> A: No, the shipping should be based on the capture result. 
>
> **Q:** **Is signature verification required after receiving payment result notification?**
>
> A: Yes. APO will send a secure callback request to you after signature verification. When verifying the signature, please note that the message to be verified should be assembled according to the standard processing: <http-method> <http-uri> <client-id>.<request-time>.<request-body>. Especially for <request-body>, the value should be taken directly instead of parsing JSON and then assembling.

### Step 5: Capture and obtain the capture result

You can configure the _paymentFactor.captureMode_ parameter in the [**pay**](https://docs.antom.com/ac/apo/pay.md) API to opt for automatic capture or manual capture. You can obtain the capture result through an asynchronous notification or inquire about the capture result by calling the [**inquiryPayment**](https://docs.antom.com/ac/apo/paymentri_online.md) API. For more information, see [Capture](https://docs.antom.com/ac/apo/capture.md).

It is recommended to save the information of the acquirer (_acquirerInfo_), refer to [Order number management](https://docs.antom.com/ac/apo_zh-cn/iddemon.md) for specific reasons. The following code shows an example of a capture result response when inquiring about the capture result:

```json
{
  "paymentResultCode":"SUCCESS",
  "paymentRequestId":"G153202503171022523041",
  "paymentResultInfo":{
    "lastFour":"1358",
    "funding":"CREDIT",
    "issuerName":"ORIENT CORPORATION",
    "expiryMonth":"**",
    "threeDSResult":{
      "cavv":"",
      "xid":"649335d6-1858-4532-8a6e-0579e1b78a2a",
      "threeDSType":"INTERNAL",
      "eci":"05",
      "threeDSVersion":"2.2.0"
    },
    "expiryYear":"**",
    "cardNo":"************1358",
    "cardBin":"428067",
    "holdName":"NIHEI SHUNSUKE",
    "issuingCountry":"JP",
    "avsResultRaw":"I",
    "fingerprint":"a28f7dd0713e2f1d4e9ccae8853f5c0abca5154a03a6559cd985eb409857579b",
    "networkTransactionId":"465076088338711",
    "cardBrand":"VISA",
    "cvvResultRaw":""
  },
  "transactions":[
    {
      "transactionType":"CAPTURE",
      "transactionStatus":"SUCCESS",
      "transactionRequestId":"G153202503171022523041",
      "transactionAmount":{
        "currency":"JPY",
        "value":"120"
      },
      "transactionTime":"2025-03-16T19:27:17-07:00",
      "acquirerInfo":{
        "referenceRequestId":"2025031719031301000600278847294",
        "acquirerTransactionId":"act_yv3fchmu3bre3l3tui5y352yby",
        "acquirerMerchantId":"pc_6cpbmm5qderubjraxrygjehf5q",
        "acquirerName":"CHECKOUT"
      },
      "transactionId":"20250317194010890100111600255676857",
      "transactionResult":{
        "resultStatus":"S",
        "resultCode":"SUCCESS",
        "resultMessage":"success"
      }
    }
  ],
  "paymentAmount":{
    "currency":"JPY",
    "value":"120"
  },
  "acquirerReferenceNo":"pay_6tmvamfcxp5ujbbfjeopyz6v7q",
  "result":{
    "resultStatus":"S",
    "resultCode":"SUCCESS",
    "resultMessage":"success."
  },
  "paymentId":"20250317194010890100111600255689113",
  "paymentResultMessage":"success.",
  "paymentTime":"2025-03-16T19:27:17-07:00",
  "acquirerInfo":{
    "referenceRequestId":"2025031719031300000600278825707",
    "acquirerTransactionId":"pay_6tmvamfcxp5ujbbfjeopyz6v7q",
    "acquirerMerchantId":"pc_6cpbmm5qderubjraxrygjehf5q",
    "acquirerName":"CHECKOUT"
  },
  "paymentStatus":"SUCCESS",
  "paymentCreateTime":"2025-03-16T19:26:42-07:00"
}
```

## After payment

### Inquire about the authorized payment result

In addition to obtaining the buyer's payment result through the asynchronous notification, you can retrieve the corresponding payment result through the payments inquiry service. You can call the [**inquiryPayment**](https://docs.antom.com/ac/apo/paymentri_online.md) API and use the _paymentRequestId_ value of the payment to check the payment status.

The following code shows how to call the [**inquiryPayment**](https://docs.antom.com/ac/apo/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 code shows an example of a request:

```json
{
    "paymentRequestId": "PAY_202506*****942875"
}
```

It is recommended to save the information on acquirers (_acquirerInfo_), refer to [Order number management](https://docs.antom.com/ac/apo_zh-cn/iddemon.md) for specific reasons. The following code shows an example of a response:

```json
{
  "paymentResultCode":"SUCCESS",
  "paymentRequestId":"G153202503171022523041",
  "paymentResultInfo":{
    "lastFour":"1358",
    "funding":"CREDIT",
    "issuerName":"ORIENT CORPORATION",
    "expiryMonth":"**",
    "threeDSResult":{
      "cavv":"",
      "xid":"649335d6-1858-4532-8a6e-0579e1b78a2a",
      "threeDSType":"INTERNAL",
      "eci":"05",
      "threeDSVersion":"2.2.0"
    },
    "expiryYear":"**",
    "cardNo":"************1358",
    "cardBin":"428067",
    "holdName":"NIHEI SHUNSUKE",
    "issuingCountry":"JP",
    "avsResultRaw":"I",
    "fingerprint":"a28f7dd0713e2f1d4e9ccae8853f5c0abca5154a03a6559cd985eb409857579b",
    "networkTransactionId":"465076088338711",
    "cardBrand":"VISA",
    "cvvResultRaw":""
  },
  "paymentAmount":{
    "currency":"JPY",
    "value":"120"
  },
  "acquirerReferenceNo":"pay_6tmvamfcxp5ujbbfjeopyz6v7q",
  "result":{
    "resultStatus":"S",
    "resultCode":"SUCCESS",
    "resultMessage":"success."
  },
  "paymentId":"2025031719401089010011160025568****",
  "paymentResultMessage":"success.",
  "paymentTime":"2025-03-16T19:27:17-07:00",
  "acquirerInfo":{
    "referenceRequestId":"202503171903130000060027882****",
    "acquirerTransactionId":"pay_6tmvamfcxp5ujbbfjeopyz****",
    "acquirerMerchantId":"pc_6cpbmm5qderubjraxrygje****",
    "acquirerName":"CHECKOUT"
  },
  "paymentStatus":"SUCCESS",
  "paymentCreateTime":"2025-03-16T19:26:42-07:00"
}
```

The table shows the possible values of _paymentStatus_ returned in the response. Please handle the result according to the guidances: 

| **_paymentStatus_** | **Message** | **Further actions** |
| --- | --- | --- |
| `SUCCESS` | Indicates that the authorized payment is successful. | You may proceed with initiating capture. |
| `FAIL` | Indicates that the authorized payment failed. | Please close the current transaction or replace _paymentRequestId_ to place the order again. |
| `PROCESSING` | Indicates that the authorized payment is being processed. | You can continue to inquire about the payment result or inquire after the payment expiration time. |

> **Common Questions**
>
> **Q:** **What are t****he key parameters in the inquiry that I need to use?**
>
> A: Pay attention to the following key parameters:
>
> -   _paymentStatus_: You need to determine the authorized payment result based on this parameter.
> -   _paymentAmo__unt_: indicates the payment amount.
>
> **Q: What is the recommended frequency for calling the** **[inquiryPayment](https://docs.antom.com/ac/apo/paymentri_online.md)** **API****?**
>
> A: It is recommended to use polling with an interval of 2 seconds. Begin polling immediately after calling the [**pay**](https://docs.antom.com/ac/apo/pay.md) API, and continue the inquiry until the final payment result is obtained or the payment result asynchronous notification is received.

### Cancel Server-side

If you need to cancel a transaction, you can use the **[cancel](http://docs.antom.com/ac/apo/paymentc_online)** API. For more information, see [Cancel](https://docs.antom.com/ac/apo/cancel.md).

### Refund Server-side

To learn about APO refund rules and how to initiate a refund for a successful transaction, see [Refund](https://docs.antom.com/ac/apo/refund.md) for more information.

### Dispute Server-side

When a buyer chooses to pay with a card, a dispute may occur. To learn more, see [Dispute](https://docs.antom.com/ac/apo/dispute.md).

### Reconciliation

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