请款

Antom 提供以下两种请款方式,您可根据业务需求自行选择:

  • 自动请款
  • 手动请款

注意韩国认证卡支付场景只支持由 Antom 自动为您发起请款。

自动请款

支付会话创建(单笔支付)支付(单笔支付)接口中 paymentFactor.captureMode 参数的默认值为 AUTOMATIC,表示买家授权完成后,由 Antom 替您立刻发起请款,两阶段中间无确认过程。 Antom 会通过 请款通知(单笔支付)将请款结果发送给您,您也可以通过调用 支付结果查询 接口查询请款结果。

手动请款

买家授权后,若您需要根据风控结果、库存等情况决策是否发起请款,可以将 支付会话创建(单笔支付)支付(单笔支付)接口paymentFactor.captureMode 参数的值设置为 MANUAL,同时需集成 请款(单笔支付)接口进行手动请款。Antom 会通过 请款通知(单笔支付)将请款结果发送给您,您也可以通过调用 支付结果查询 接口查询请款结果。

集成步骤

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

  1. 发起请款请求
  2. 获取请款结果

步骤 1:发起请款请求 服务端

授权成功后,您需要调用 请款(单笔支付)接口进行请款,并在请求中传入以下参数:

参数名称

是否必需

描述

captureRequestId

请款请求的单号。每次请款请求必须生成新的值。

captureAmount.currency

请款币种。该参数的值需与 支付会话创建(单笔支付)支付(单笔支付)请求中 paymentAmount.currency 的值保持一致。

captureAmount.value

请款金额。该参数的值需与 支付会话创建(单笔支付)支付(单笔支付)请求paymentAmount 的值保持一致。

paymentId

授权阶段的 Antom 单号。该参数的值需与 支付会话创建(单笔支付)支付(单笔支付)响应中的 paymentId 或者 支付通知 请求中 paymentId 保持一致。

以下是调用 请款(单笔支付)接口的示例代码:

copy
public static void capture() {
    AlipayCaptureRequest alipayCaptureRequest = new AlipayCaptureRequest();

    Amount amount = Amount.builder().currency("USD").value("2000").build();
    alipayCaptureRequest.setCaptureAmount(amount);

    // 替换为您的 captureRequestId
    String captureRequestId = UUID.randomUUID().toString();
    alipayCaptureRequest.setCaptureRequestId(captureRequestId);

    // 替换为您的 paymentId
    alipayCaptureRequest.setPaymentId("20241212********0211082739");

    AlipayCaptureResponse alipayCaptureResponse = null;
    try {
        alipayCaptureResponse = CLIENT.execute(alipayCaptureRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
    }
}

以下为请款请求的代码示例:

copy
{
    "paymentId": "20241212********0211082739",
    "captureRequestId": "4c6c8ffd-*******eeb5af0e3f4",
    "captureAmount": {
        "currency": "USD",
        "value": "2000"
    }
}

以下为请款响应的代码示例:

copy
{
    "captureAmount": {
        "currency": "USD",
        "value": "2000"
    },
    "captureId": "2024121219********670209694544",
    "captureRequestId": "4c6c8ffd-*******eeb5af0e3f4",
    "captureTime": "2024-12-11T23:34:03-08:00",
    "paymentId": "20241212********0211082739",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success",
        "resultStatus": "S"
    }
}

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

result.resultStatus

信息

后续操作

S

请款成功。

推进订单状态,同时存储 Antom 侧的 captureId 用于后续退款。

U

请款处理中。

建议等待 请款通知(单笔支付)接口的请款结果通知或使用 支付结果查询 接口主动查询请款结果。

F

请款失败。

建议根据错误码排查原因并重试。如果问题未解决,请联系 Antom 技术支持。

注意:如果您未收到响应报文,可能是网络超时所致。建议等待 请款通知(单笔支付)接口的请款结果通知或使用 支付结果查询 接口主动查询请款结果。

步骤 2:获取请款结果 服务端

您可以通过以下方法之一获取请款结果:

  • 接收异步通知
  • 主动查询结果

注意:请款成功或失败时,Antom 均会向您设置的 webhook URL 发送异步通知,但如果 请款(单笔支付)接口的参数异常且同步返回 F 时,不会触发异步通知。

进行请款后,请款通知(单笔支付)接口会向您发送请款结果通知,请按照以下步骤进行配置:

  1. 设置接收通知的 webhook URL

您可以选择以下两种方法中的一种来设置接收通知的 webhook URL(若两者都设置,则以请求中指定 URL 的优先):

  • 若您在 支付会话创建(单笔支付) 支付(单笔支付)接口请求的 paymentNotifyUrl 参数已传入该笔订单的异步通知接收 URL,则请款和授权结果通知都会传入此 URL。暂不支持通过接口单独传入请款结果的通知地址。
  • 若您的所有订单有统一的通知 URL,您可以在 Antom Dashboard > 开发者 > 通知地址 中设置 webhook URL,此时请款结果通知和授权结果通知分为两个接收地址。Antom Dashboard 仅支持固定 URL 的配置,不支持包含订单号等参数信息的 URL。具体操作请参阅通知地址

请款通知请求体中包含以下重点参数:

  • resultStatus:必传。订单请款的结果,也是您发货的最终依据。
  • captureId:Antom 为识别请款分配的专属 ID。
  • captureAmount请款金额。

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

copy
{
  "captureAmount": {
    "currency": "USD",
    "value": "2000"
  },
  "captureId": "2024121219********670209694544",
  "captureRequestId": "4c6c8ffd-*******eeb5af0e3f4",
  "captureTime": "2024-12-11T23:34:03-08:00",
  "paymentId": "20241212********0211082739",
  "notifyType": "CAPTURE_RESULT",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

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

result.resultStatus

信息

后续操作

S

请款成功。

推进订单状态,同时存储 Antom 侧的 captureId 用于后续退款。

F

请款失败。

建议更换 captureRequestId 重试请款。如果问题未解决,请联系 Antom 技术支持。

  1. 异步通知验签

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

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

copy
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) {

        // 从 HTTP 请求中获取所需的参数
        String requestUri = request.getRequestURI();
        String requestMethod = request.getMethod();

        // 从请求头中获取所需的参数
        String requestTime = request.getHeader("request-time");
        String clientId = request.getHeader("client-id");
        String signature = request.getHeader("signature");

        Result result;
        AlipayResponse response = new AlipayResponse();

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

            // 反序列化通知体

            // 根据通知结果更新订单状态

            // 向服务器响应已接收通知
            result = new Result("SUCCESS", "success", ResultStatusType.S);

        } catch (Exception e) {
            String errorMsg = e.getMessage();
            // 处理错误情况
            result = new Result("ERROR", errorMsg, ResultStatusType.F);
        }
        response.setResult(result);
        return ResponseEntity.ok().body(response);
    }

}

无论订单是否请款成功,每个通知请求均需按以下固定格式响应。否则,Antom 会重新发送异步通知。

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

常见问题

问:自动请款模式下,授权成功后多久会收到请款结果的通知?

答:授权成功后,Antom 会立刻为您发起请款,并在秒级内将请款结果通过异步通知发送给您。

问:手动请款模式下,授权成功后多久需要发起请款?

答:如您自行调用 请款(单笔支付)接口发起手动请款,请在授权成功后最迟 7 天内完成操作。若超过 7 天未发起请款,Antom 将自动撤销该笔订单并为买家解冻资金。

问:调用请款接口时支持原请求重试吗?
答:基于 captureRequestId 的幂等性设计,重试规则如下:

  • 首次返回 resultStatus=S(成功),无需再次重试。
  • 首次返回 resultStatus=F(失败),不支持重试,重复请求仍返回失败状态。
  • 首次返回 resultStatus=U(处理中),支持重试,再次请求将返回最终处理结果(成功 S/失败 F)。

问:请款结果的通知地址是否需要与授权结果的通知地址保持一致?
答:默认情况下,请款结果与授权结果的通知地址保持一致。如需将请款结果通知发送至独立的 URL,您可以在 Antom Dashboard 里配置通知地址。配置请款通知地址限制如下:

  • Antom Dashboard 仅支持固定 URL 的配置,不支持包含订单号等参数信息的 URL。
  • 暂不支持通过接口单独传入请款结果的通知地址。

问:授权已完成,但收到请款失败的通知时,应该如何处理?
答:请款通知失败代表最终未发生资金转移,您可以撤销授权避免买家资金冻结,同时联系 Antom 技术支持进行排查。

问:请款金额需要和授权金额保持一致吗?
答:两者需要保持一致。目前仅支持全额请款,不支持部分请款。

问:通过 请款(单笔支付)接口发起手动请款后,未收到请款结果通知,应该如何处理?
答:您可以通过 支付结果查询 接口主动查询请款结果。避免重复发起请款请求,当一笔订单处于 CAPTURE_IN_PROCESS 时,再次调用将触发“金额超限”错误。

问:使用 支付结果查询 接口查询请款状态时,应注意哪些关键参数?

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

  • result:仅表示本次 支付结果查询 接口的调用结果。对于 APM 支付,订单的支付结果需要根据 paymentStatus 进行判断(SUCCESS 成功/FAIL 失败/PROCESSING 处理中)。对于卡支付、Apple Pay 和 Google Pay,paymentStatus 仅代表授权结果,是否发货需要依赖请款结果决策。
  • transactions[] 数组中,筛选出 transactionType = CAPTURE(在手动请款模式下,还需确保 transactionRequestId 与您请款请求中的 captureRequestId 完全匹配)的交易,则其对应的 transactionResult.resultStatus 为请款结果。
  • paymentAmount:用于核对支付金额。
  • paymentId:表示由 Antom 生成的支付订单 ID,用于退款和对账。

问:我应该多久调用一次 支付结果查询 接口?

答:以 2 秒的间隔持续调用 支付结果查询 接口,直到获取最终的支付结果或收到异步支付结果通知为止。

问:在自动请款模式下,若您通过 paymentExpireTime 自行设定的超时时间已到,买家完成了授权但 Antom 尚未完成请款,将如何处理?

答:根据交易的实际处理进度,按以下不同情况处理:

  • 若在超时前授权和请款均已完成:Antom 将向您发送授权结果通知和请款结果通知。此时,查询结果中请款状态为成功 S
  • 若超时前授权已发起但未返回最终结果:Antom 收到渠道的授权结果后,会自动撤销该笔交易。此时,查询结果中授权状态为失败 F 且无请款状态。
  • 若超时前授权完成,但自动请款发起时间达到或晚于超时时间:Antom 不会发起请款。您会收到授权结果通知,但不会收到请款结果通知。此时,查询结果中无请款状态。建议您主动取消交易,以释放买家的冻结资金。
  • 若超时前授权完成且请款已发起,但请款结果未返回:Antom 收到渠道的请款结果后,会自动对该笔交易发起退款。此时,查询结果中请款状态为失败 F 且退款状态为成功 S

如您未设置 paymentExpireTime,Antom 默认超时时间为 14 分钟,买家在此时间内完成授权即可,请款发起时间不受该时限影响。