韩国认证卡支付

本文详细描述了如何通过托管模式实现韩国认证卡支付的集成。托管模式能够有效保护您免受支付卡行业数据安全标准(PCI-DSS)合规要求的限制。这是因为买家的敏感支付数据不会经过您的服务器进行处理或存储,从而避免了潜在的安全风险和数据泄露隐患。此外,该模式无需您单独进行 PCI 合规认证,节约了运营和技术成本,同时提升了支付系统的部署效率。

在托管模式中,APO 为您生成并提供了一个用户友好的支付信息收集页面。买家在支付流程中将被安全地重定向至该页面,用于输入其支付信息。在韩国认证卡支付场景下,每一笔支付均需完成认证流程。借助托管模式,您无需直接接触买家的支付数据,同时也能够在保障安全性合规性的前提下,为买家提供无缝和可靠的支付体验。

用户体验

以下图示展示了 APO 接入发行卡认证支付的用户体验:

Web:

web.png

App:

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

支付流程

对于使用韩国认证卡支付,支付流程包括以下步骤:

韩国认证卡时序图-中.png

  1. 买家选择商品后下单。
  2. 发起支付请求。
    商户服务端调用 支付 接口以获取支付链接。
  3. 买家跳转至支付方式页面。
    APO 从 支付 接口的响应中返回支付方式中间页(normalUrl),并由商户客户端展示支付方式中间页,供您的买家选择所需的韩国认证卡品牌。随后,买家跳转至所选的韩国认证卡支付页面,完成支付后返回商户支付结果页。
  4. 获取支付结果。
    商户服务会接收 APO 返回的支付结果通知,并根据结果进行相应的业务处理。
  5. 请款并获取请款结果
    若收单机构为 Antom,Antom 会在您支付成功后自动发起请款。您可以通过以下两种方法之一获取请款结果
    • 异步通知:在 支付 接口中设置 paymentNotifyUrl 参数,以指定接收异步通知的地址。当支付成功或过期时,APO 会使用 请款通知 向您发送异步通知。
    • 手动查询:调用 支付结果查询 接口来查询支付请求状态。

注意若您在多机构下开通了同一个支付方式,请在 APO Dashboard 完成路由配置。若不配置,则 APO 会采取随机路由规则。

集成准备

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

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

集成步骤

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

  1. 发起支付请求
  2. 跳转至支付方式中间页(normalUrl
  3. 获取支付结果
  4. 请款并获取请款结果

步骤 1:发起支付请求 服务端

当买家进行支付时,您需要收集关键信息,如支付请求 ID、订单信息、支付重定向链接和支付结果通知链接,调用 支付 接口提交支付请求,并将支付方式中间页的链接返回给商户前端进行跳转。

1. 调用支付接口

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

copy
public static void payByCard() {
    AlipayPayRequest alipayPayRequest = new AlipayPayRequest();
    alipayPayRequest.setProductCode(ProductCodeType.CASHIER_PAYMENT);

    // 替换为您的 paymentRequestId
    String paymentRequestId = UUID.randomUUID().toString();
    alipayPayRequest.setPaymentRequestId(paymentRequestId);

    // 设置金额
    Amount amount = Amount.builder().currency("SGD").value("4200").build();
    alipayPayRequest.setPaymentAmount(amount);

    // 设置支付方式
    PaymentMethod paymentMethod = PaymentMethod.builder().paymentMethodType("CARD").build();
    alipayPayRequest.setPaymentMethod(paymentMethod);

    // 替换为您的 orderId
    String orderId = UUID.randomUUID().toString();

    // 设置买家信息
    Buyer buyer = Buyer.builder().referenceBuyerId("yourBuyerId").build();

    // 设置订单信息
    Order order = Order.builder().referenceOrderId(orderId)
    .orderDescription("antom testing order").orderAmount(amount).buyer(buyer).build();
    alipayPayRequest.setOrder(order);

    // 设置设备环境信息
    Env env = Env.builder().terminalType(TerminalType.WEB).clientIp("1.2.3.4").build();
    alipayPayRequest.setEnv(env);

    // 设置授权请款支付模式
    PaymentFactor paymentFactor = PaymentFactor.builder().isAuthorization(true).build();
    alipayPayRequest.setPaymentFactor(paymentFactor);

    // 替换为您的通知地址
    alipayPayRequest.setPaymentNotifyUrl("https://www.yourNotifyUrl.com");

    // 替换为您的跳转地址
    alipayPayRequest.setPaymentRedirectUrl("https://www.yourMerchantWeb.com");

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

2. 发起支付请求

以下是支付请求参数重点参数:

参数名称

是否必需

描述

productCode

在此场景中,值设置为 CASHIER_PAYMENT

paymentRequestId

商家为识别支付请求而分配的专属 ID,每次发起支付都要有一个新的 ID。

paymentAmount

支付金额,需按下单币种的最小单位设置,如 CNY 为分,KRW 为元。

paymentMethod.paymentMethodType

支付方式选项中包含的支付方式类型。卡支付场景下固定为 CARD

paymentRedirectUrl

商户端支付结果页,需根据服务端结果展示,非固定为成功页。

paymentNotifyUrl

支付结果通知地址,可以在接口传入,也可以在 APO Dashboard 里设置一个固定值。

settlementStrategy

该笔支付的结算币种,若业务签约了多个结算币种,需在接口中指定。仅 Antom 收单机构生效。

order.buyer

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

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

order.referenceOrderId

商户端订单号。

order.orderDescription

商户端订单描述。

env.terminalType

指定买家发起交易的端类型。有效值为:

  • WEB:商户 PC 网站。
  • WAP:商户 H5 网站。
  • APP:商户 app。

env.osType

买家发起交易的环境。当买家在商户手机浏览器网站发起时,此参数值则为 ANDROID 或者 IOS

env.clientIp

买家当前的 IP。卡支付场景下,需提供买家的 IP 信息。

paymentFactor.isAuthorization

表示支付场景是否为授权场景。在接入不同的收单机构时,各收单机构配置此参数的条件不同:

  • Antom:固定传 true,指定为授权请款模式。
  • 2C2P:不需要传入。

paymentExpiryTime

支付有效期。

selectedCardBrand

仅针对 Antom 生效。指定 selectedCardBrand 参数时需注意以下情况:

  • 商户提供银行列表时需指定 selectedCardBrand 为具体的卡品牌。
  • 使用 APO 提供银行列表时无需传入此参数。

请参见银行卡品牌以获取银行卡品牌列表。

requireIssuerAuthentication

对于韩国本地卡认证场景,该参数固定为 true

注意:有关完整参数的更多信息以及某些支付方式额外要求,请参阅 支付 接口。

以下代码展示了向 Antom 收单机构和 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,//指定为 true,仅 Antom 收单机构需要。
  },
  "paymentMethod": {
    "paymentMethodType": "CARD",
    "paymentMethodMetaData": {
      "paymentMethodRegion": "KR",
      "requireIssuerAuthentication": true,
      "selectedCardBrand": "SAMSUNG" //可选参数,用于指定卡品牌,仅 Antom 收单机构支持
    }
  },
  "paymentNotifyUrl": "https://www.yourNotifyUrl.com",
  "paymentRedirectUrl": "https://www.yourMerchantWeb.com",
  "paymentRequestId": "paymentRequestId01",
  "productCode": "CASHIER_PAYMENT"
}

以下代码展示了发起支付后 Antom 收单机构和 2C2P 收单机构可能返回的响应报文示例,其中包含以下重点参数:

  • acquirerInfo收单机构单号信息。建议储存该参数信息,具体原因请查看单号说明
  • result.resultStatus:支付的状态。
  • normalUrl: 商户前端需要处理跳转的支付页面链接。
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"
}

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

result.resultStatus信息下一步操作

F

表示支付失败。

请检查并验证当前接口所需的请求字段(包括头部字段和正文字段)是否正确传递并有效。

U

表示支付进行中。

支付 接口的响应中获取 normalUrl 信息,并由商户前端跳转到 normalUrl 链接

注意:如果您未收到响应报文,可能是网络超时所致。请更换 paymentRequestId 重新调用接口以解决问题。

常见问题

问:acquirerInfo 里的信息作用是什么?应该如何消费?

答:acquirerInfo 里的信息代表是收单机构的信息,请查看单号说明文档。

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

答:为了避免特定支付方式的兼容性问题,请求中的字段请勿使用中文字符。

步骤 2:跳转至支付方式中间页(normalUrl客户端

当商户服务端拿到返回的支付中间页链接 (normalUrl) 后,需将该 normalUrl 链接传递给前端,由商户前端跳转至韩国认证卡支付中间页页面。

中间页面.png

获取 normalUrl 后,您需要在浏览器将页面跳转至韩国认证卡支付中间页页面,或在新标签页打开。

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

注意:因为不同银行可能会需要拉起银行 APP,如果从商户 APP 端发起,建议外跳到 normalUrl 处理.

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

注意:当支付完成后,

  • 对于 Antom 收单机构,APO 会发送支付异步通知和请款异步通知,您需要以请款异步通知作为发货依据。
  • 对于 2C2P 收单机构,APO 仅会发送支付异步通知,您需要以支付成功作为发货依据。

1. 设置接收通知的 webhook URL

您可以选择以下两种方法中的一种来设置接受通知的 webhook URL:

  • 若您的每个订单都有单独的通知 URL,建议您在每笔请求中设置 webhook URL。您可以通过 支付 接口请求的 paymentNotifyUrl 参数传入该笔订单的接收异步通知 URL。
  • 若您的所有订单都有一个统一的通知 URL,您则可以APO Dashboard开发者 > 通知地址 中设置 webhook URL。

以下代码展示了一个支付结果异步通知的请求报文示例:

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"
    }
}

您可能会收到请求报文中 result.resultStatus 参数的不同值,请您根据下表指引进行处理:

result.resultStatus

信息下一步操作

S

表示支付成功。

您可以储存以下关键信息:

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

F

表示支付失败。

请您引导买家重新下单。

2. 异步通知验签

若您收到 APO 的异步通知,您需要对 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();
}

您无需对通知结果响应做加签处理,但是对于每个通知请求均需按以下固定格式响应,与订单支付成功与否无关。

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

常见问题

问:何时会发送通知?

答:这取决于支付是否完成。如果支付完成后,在收到收单机构的通知后 APO 会立即发送异步通知

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

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

  • 如果由于网络原因未收到异步通知。 
  • 如果您收到来自 APO 的异步通知,但您没有按照处理通知的示例代码格式对通知做出响应。 

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

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

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

步骤 4:请款和获取请款结果

您可以通过配置 支付 接口中的 paymentFactor.captureMode 参数进行全款自动请款。您可以在请款后通过异步通知或主动查询来获取请款结果。参见请款了解更多详情。

建议您保存收单机构相关信息( acquirerInfo 参数),具体原因请查看单号说明。以下代码展示了主动查询请款结果的响应报文示例:

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"
    }
}

支付后操作

查询支付结果 服务端

除了可以通过异步通知的功能获取支付结果,同时也支持您通过主动查询服务来获取对应的结果。您可以调用 支付结果查询 接口,使用支付的 paymentRequestId 查询支付状态。

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

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

    // 替换为您的 paymentRequestId
    alipayPayQueryRequest.setPaymentRequestId("yourPaymentRequestId");

    AlipayPayQueryResponse alipayPayQueryResponse = null;
    try {
        alipayPayQueryResponse = CLIENT.execute(alipayPayQueryRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // 处理错误情况
    }
}

以下代码展示了一个请求报文的示例:

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

以下代码展示了响应报文的示例。建议保存收单机构相关信息( acquirerInfo 参数),具体原因请查看单号说明

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"
}

您可能会收到响应报文中 paymentStatus 参数的不同值,请您根据下表指引进行处理:

paymentStatus

信息

下一步操作

SUCCESS

表示支付成功。

您可以发起请款。

FAIL

表示支付失败。

请关闭订单或更换 paymentRequestId 再次尝试。

PROCESSING

表示支付进行中。

您可以继续查询或在关单时间后查询。

常见问题

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

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

  • paymentStatus : 您需要根据此参数来判断支付状态。
  • paymentAmount :表示支付的金额。 

问:建议发起查询的频率是多久?

答:推荐以轮询的形式发起查询,间隔 2 秒。从发起下单 支付 接口后立即发起轮询,直到查询到最终的支付结果或收到支付结果异步通知为止。

取消交易 服务端

如果需要取消某笔交易,您可以在支持的取消窗口期内通过 取消支付 接口进行操作,详情请参见取消交易

退款 服务端

若您需要了解 APO 的退款规则及如何对成功的交易发起退款,详情请参见退款

争议 服务端

若买家选择使用卡支付方式,会涉及到争议相关的集成,详情请参见争议。

对账 服务端

交易完成后,使用 APO 提供的财务报告进行对账。有关如何对账和 APO 结算规则的更多信息,请参阅对账