Accept payments using South Korean cards

This article describes how to integrate with South Korean issuer-authenticated cards through the hosted mode. The hosted mode protects you from the requirements of PCI-DSS compliance by ensuring that sensitive buyer payment data does not pass through or get stored on your servers. This eliminates security risks and data breach concerns. Additionally, you do not need to undergo separate PCI compliance certification, reducing operational and technical costs while improving deployment efficiency for your payment system.

In the hosted mode, APO generates and provides you with a user-friendly payment information collection page. During the South Korean issuer-authenticated card payment process, each payment requires authentication. With the hosted mode, you avoid direct contact with buyers' payment data while delivering a seamless and reliable payment experience that ensures both security and compliance.

User experience

The diagram below shows the user experience for South Korean issuer-authenticated card payments integrated through APO.

Web:

web.png

App:

7f0c9258-cd88-49cd-837c-c66f06841de9.png

Payment flow

The payment flow of the South Korean issuer-authenticated card payment integration is composed of the following steps:

时序图-英.png

  1. The buyer selects goods and places an order.
  2. Create a payment request.
    The merchant server calls the pay API to obtain the payment link.
  3. Redirect to the payment page.
    APO returns the payment page (normalUrl) from the pay API response. The merchant’s client displays the payment page for the buyer to select the desired South Korean issuer-authenticated card brand. The buyer is then redirected to the selected issuer-authenticated card payment page to complete the payment and return the merchant result page.
  4. Obtain the payment result.
    The merchant server receives the payment result notification returned by APO, and processes the order based on the returned result.
  5. Capture and obtain the capture result.
    If the acquirer is Antom, Antom will automatically intiate capture after the payment is successful. You can obtain the capture result using one of the following methods:
    • Asynchronous notification: Specify paymentNotifyUrl in the pay API to set the address for receiving asynchronous notifications. When the payment request is successful or expires, APO uses the notifyCapture API to send asynchronous notifications to you.
    • Synchronous inquiry: Call the inquiryPayment 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. If no payment routing rules are set, APO will use random routing rules.

Integration preparations

Before you start integrating, read the Integration Guide and API Overview 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
  • 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.

Integration steps

Start your integration by taking the following steps.

  1. Initiate a payment request
  2. Redirect to the payment page (normalUrl)
  3. Obtain the authorized payment result
  4. Capture and obtain the capture result

Step 1: Create a payment request Server-side

When the buyer makes a payment, you need to collect the key order information, such as the paymentRequestId, order information, normalUrl, and paymentNotifyUrl. Call the pay API to submit a payment request. The merchant's frontend uses the returned payment page URL (normalUrl) for redirection.

1. Call the pay API

The following sample code shows how to call the pay API:

copy
public static void payByCard() {
    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);

    // 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 capture authorization 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
    }
}

2. Create a payment request

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.paymentMethodType

Yes

The types of payment methods included in the payment method options. 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.

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

Indicates whether the payment scenario is authorization.

When integrating with different acquirers, the conditions for configuring this parameter vary:

  • Antom: The value of this parameter is fixed as true, specifying the capture-authorized mode.
  • 2C2P: This parameter is not required.
paymentExpiryTime

No

The date and time when the payment expires.

selectedCardBrand

No

Applies to Antom only. When specifying the selectedCardBrand parameter, note the following:

  • When the merchant provides the bank list, selectedCardBrand must be set to a specific card brand.
  • When using the bank list provided by APO, this parameter is not required.

See Card Brands for the list of card brands.

requireIssuerAuthentication

Yes

For South Korean issure-authenticated card payments, this parameter is fixed as true.

Note: For more information about the full parameters and other requirements of specific payment methods, refer to the pay API.

The following shows the sample code of submitting a payment request to Antom and 2C2P:

copy
{
  "env": {
    "clientIp": "1.2.3.4",
    "terminalType": "WEB"
  },
  "order": {
    "buyer": {
      "referenceBuyerId": "yourBuyerId"
    },
    "orderAmount": {
      "currency": "KRW",
      "value": "4200"
    },
    "orderDescription": "antom testing order",
    "referenceOrderId": "referenceOrderId01"
  },
  "paymentAmount": {
    "currency": "KRW",
    "value": "4200"
  },
  "paymentFactor": {
    "isAuthorization": true,//Set as true. Applies to Antom only. 
  },
  "paymentMethod": {
    "paymentMethodType": "CARD",
    "paymentMethodMetaData": {
      "paymentMethodRegion": "KR",
      "requireIssuerAuthentication": true,
      "selectedCardBrand": "SAMSUNG" //Optional, used to specify card brands. Applies to Antom only.
    }
  },
  "paymentNotifyUrl": "https://www.yourNotifyUrl.com",
  "paymentRedirectUrl": "https://www.yourMerchantWeb.com",
  "paymentRequestId": "paymentRequestId01",
  "productCode": "CASHIER_PAYMENT"
}

The following shows the sample code of a response from Antom and 2C2P, 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 for specific reasons.
  • result.resultStatus: The payment status.
  • normalUrl: The URL used to redirect to the payment page.
copy
{
    "result": {
        "resultStatus": "U",
        "resultCode": "PAYMENT_IN_PROCESS",
        "resultMessage": "payment in process"
    },
    "normalUrl": "https://iexpfront-sea-global.alipay.com/payments/method/checkout/?requestId=y1%2FIeV52JEJZMg0qbEBA9%2BKavJ4ht0f9b6DDjEImUtbHQintJ1M4FuCQnwG%2BXg4z&merchantId=188acbhclgRbFaL97GwPJ5dXhJ9TNc9ZeJlPW1HhaJBprU%3D",
    "paymentId": "2025021219401090000018822xxxxxx",
    "paymentRequestId": "amsdmpay_${userConfig_user}xxxxxxx",
    "paymentResultInfo": {

    },
    "acquirerInfo": {
        "referenceRequestId": "amsdmpay_${userConfig_user}xxxxxx",
        "acquirerResultCode": "PAYMENT_IN_PROCESS",
        "acquirerTransactionId": "2025021219401090000018822027xxxxxx",
        "acquirerResultMessage": "Payment is processing.",
        "acquirerMerchantId": "218812012058xxxxx",
        "acquirerName": "ALIPAY"
    },
    "paymentAmount": {
        "currency": "KRW",
        "value": "1000"
    },
    "paymentCreateTime": "2025-02-11T23:55:46-08:00"
}

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.resultStatusMessageFurther actions

F

Indicates the payment failed.

Please check and verify whether the required request fields for the current API (including header fields and body fields) are correctly passed and valid.

U

Indicates that the payment is being processed.

Obtain the normalUrl information from the pay API response. The merchant’s front end redirect the buyer to the normalUrl URL.

Note: If you did not receive a response message, it might be due to a network timeout. You can change paymentRequestId and call the pay API again.

Common Questions

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 for more information.

Q: Are Chinese characters allowed in the values of the request parameters?

A: To prevent potential compatibility issues with specific payment methods, Chinese characters should not be used in any request fields.

Step 2: Redirect to the payment page (normalUrl) Client-side

Once the merchant’s server obtains the payment page URL (normalUrl), pass normalUrl to the frontend. The merchant’s frontend will redirect the buyer to the South Korean issuer-authenticated card payment page.

中间页面.png

After obtaining normalUrl, you need to redirect the page to the South Korean issuer-authenticated payment page in the browser, or open it in a new tab.

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

Note: When the payment is initiated from the merchant's app, certain banks may launch their own banking apps. It is recommended to perform an external redirection to normalUrl.

Step 3: Obtain the payment result Server-side

Note: After the payment is complete,

  • for Antom, APO will send both payment and capture asynchronous notifications. You should use the capture asynchronous notification as the basis for shipment.
  • for 2C2P, APO will only send payment asynchronous notifications. You should use payment success result as the basis for shipment.

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 API.
  • If all your orders share a unified notification URL, you can set the webhook URL on APO Dashboard through Developer > Notification Address.

The following is the notification request sample code:

copy
{
    "acquirerInfo": {
        "acquirerMerchantId": "2188120021519xxx",
        "acquirerName": "ALIPAY",
        "acquirerResultCode": "SUCCESS",
        "acquirerResultMessage": "success",
        "acquirerTransactionId": "202506191940109000001885H0250xxxxxx",
        "referenceRequestId": "PAYMENT_20250619112xxxxxx_AUTO"
    },
    "notifyType": "PAYMENT_RESULT",
    "paymentAmount": {
        "currency": "KRW",
        "value": "1000"
    },
    "paymentCreateTime": "2025-06-18T20:29:07-07:00",
    "paymentId": "202506191940109000001885H0250xxxxxx",
    "paymentRequestId": "PAYMENT_20250619112xxxxxx_AUTO",
    "paymentResultInfo": {
        "avsResultRaw": "",
        "cardNo": "************1234",
        "cvvResultRaw": "",
        "paymentMethodRegion": "KR",
        "threeDSResult": {
            "cavv": "",
            "challengCancel": "",
            "eci": "",
            "threeDSOffered": false,
            "threeDStransactionStatusReason": ""
        }
    },
    "paymentTime": "2025-06-18T20:29:25-07: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 payment is successful.

You can store the following key information:

  • paymentRequestId: The payment request Id used for inquiry, refund and reconciliation.
  • paymentId: The payment Id generated by APO, used for refund and reconciliation.
  • paymentAmount: indicates the payment amount.

F

Indicates the payment failed.

Please guide the buyer to place the order again.

2. Verify asynchronous notifications

If you receive an asynchronous notification from APO, you are required to return the response in the Sample code format, but you do not need to countersign the response.

You need to verify the signature of the payment notification sent by APO.

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

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

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

        // deserialize the notification body
        JSONObject jsonObject = JSON.parseObject(notifyBody);
        String notifyType = (String)jsonObject.get("notifyType");
        if("PAYMENT_RESULT".equals(notifyType)){
            AlipayPayResultNotify paymentNotify = jsonObject.toJavaObject(AlipayPayResultNotify.class);
            if (paymentNotify != null && "SUCCESS".equals(paymentNotify.getResult().getResultCode())) {
                // handle your own business logic.
                // e.g. The relationship between payment information and users is kept in the database.
                System.out.println("receive payment notify: " + JSON.toJSONString(paymentNotify));
                return Result.builder().resultCode("SUCCESS").resultMessage("success.").resultStatus(ResultStatusType.S).build();
            }
        }else if("CAPTURE_RESULT".equals(notifyType)){
            AlipayCaptureResultNotify captureNotify = jsonObject.toJavaObject(AlipayCaptureResultNotify.class);
            if (captureNotify != null && "SUCCESS".equals(captureNotify.getResult().getResultCode())) {
                // handle your own business logic.
                System.out.println("receive capture notify: " + JSON.toJSONString(captureNotify));
                return Result.builder().resultCode("SUCCESS").resultMessage("success.").resultStatus(ResultStatusType.S).build();
            }
        }

    } catch (Exception e) {
        // handle error condition
        return Result.builder().resultCode("FAIL").resultMessage("fail.").resultStatus(ResultStatusType.F).build();
    }

    return Result.builder().resultCode("SYSTEM_ERROR").resultMessage("system error.").resultStatus(ResultStatusType.F).build();
}

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.

copy
{
    "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 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: 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 4: Capture and obtain the capture result

You can configure the paymentFactor.captureMode parameter in the pay API to opt for automatic capture. You can obtain the capture result through an asynchronous notification or inquire about the capture result by calling the inquiryPayment API. For more information, see Capture.

It is recommended to save the information of the acquirer (acquirerInfo), refer to Order number management for specific reasons. The following code shows an example of a capture result response when inquiring about the capture result:

copy
{
    "acquirerInfo": {
        "acquirerMerchantId": "2188120021519xxx",
        "acquirerName": "ALIPAY",
        "acquirerResultCode": "SUCCESS",
        "acquirerResultMessage": "success",
        "acquirerTransactionId": "202506191940108070001885H0270xxxxxx",
        "referenceRequestId": "PAYMENT_20250619112xxxxxx_AUTO"
    },
    "captureAmount": {
        "currency": "KRW",
        "value": "1000"
    },
    "captureId": "202506191940108070001885H0270xxxxxx",
    "captureRequestId": "PAYMENT_20250619112xxxxxx_AUTO",
    "captureTime": "2025-06-18T20:29:26-07:00",
    "notifyType": "CAPTURE_RESULT",
    "paymentId": "202506191940109000001885H0250xxxxxx",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

After payment

Inquire about the authorized payment result Server-side

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 API and use the paymentRequestId value of the payment to check the payment status.

The following code shows how to call the inquiryPayment API:

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

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

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

The following code shows an example of a request:

copy
{
    "paymentRequestId": "PAY_202506*****942875"
}

It is recommended to save the information on acquirers (acquirerInfo), refer to Order number management for specific reasons. The following code shows an example of a response:

copy
 {
    "cardInfo": {

    },
    "paymentResultCode": "SUCCESS",
    "paymentRequestId": "amsdmpay_${userConfig_user}xxxxxx",
    "paymentResultInfo": {
        "paymentMethodRegion": "KR",
        "avsResultRaw": "",
        "threeDSResult": {
            "cavv": "",
            "eci": ""
        },
        "cardNo": "37918****32*",
        "cvvResultRaw": ""
    },
    "transactions": [
        {
            "transactionType": "CAPTURE",
            "transactionStatus": "SUCCESS",
            "transactionRequestId": "amsdmpay_${userConfig_user}_20250211_235542_802",
            "transactionAmount": {
                "currency": "KRW",
                "value": "1000"
            },
            "transactionTime": "2025-02-11T23:58:20-08:00",
            "acquirerInfo": {
                "referenceRequestId": "amsdmpay_${userConfig_user}_20250211_23xxxxxx",
                "acquirerResultCode": "SUCCESS",
                "acquirerTransactionId": "2025021219401080700018822027xxxxxx",
                "acquirerResultMessage": "success",
                "acquirerMerchantId": "21881201205xxxxxx",
                "acquirerName": "ALIPAY"
            },
            "transactionId": "202502121940108070001882xxxxxxxx",
            "transactionResult": {
                "resultStatus": "S",
                "resultCode": "SUCCESS",
                "resultMessage": "success"
            }
        }
    ],
    "paymentAmount": {
        "currency": "KRW",
        "value": "1000"
    },
    "result": {
        "resultStatus": "S",
        "resultCode": "SUCCESS",
        "resultMessage": "success."
    },
    "authExpiryTime": "2025-02-18T23:58:18-08:00",
    "paymentId": "2025021219401090000018822xxxxxxx",
    "paymentRedirectUrl": "http://gol.alipay.net:8080/amsdemo/result?paymentRequestId=amsdmpay_${userConfig_user}_20250211_235542_802",
    "paymentResultMessage": "success.",
    "paymentTime": "2025-02-11T23:58:19-08:00",
    "acquirerInfo": {
        "referenceRequestId": "amsdmpay_${userConfig_user}_20250211_235542_802",
        "acquirerResultCode": "SUCCESS",
        "acquirerTransactionId": "202502121940109000001882202xxxxxxx",
        "acquirerResultMessage": "success",
        "acquirerMerchantId": "218812012058xxxx3",
        "acquirerName": "ALIPAY"
    },
    "paymentMethodType": "CARD",
    "paymentStatus": "SUCCESS"
}

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 payment is successful.

You may proceed with initiating capture.

FAIL

Indicates that the payment failed.

Please close the current transaction or replace paymentRequestId to place the order again.

PROCESSING

Indicates that the 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 the 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.
  • paymentAmount: indicates the payment amount.

Q: What is the recommended frequency for calling the inquiryPayment API?

A: It is recommended to use polling with an interval of 2 seconds. Begin polling immediately after calling the pay 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 API. For more information, see Cancel.

Refund Server-side

To learn about APO refund rules and how to initiate a refund for a successful transaction, see Refund for more information.

Dispute Server-side

When a buyer chooses to pay with a card, a dispute may occur. To learn more, see Dispute.

Reconciliation Server-side

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.