请款

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

  • 自动请款
  • 手动请款

自动请款

支付会话创建(收银台) 支付(收银台)接口中 paymentFactor.captureMode 参数的默认值为 AUTOMATIC,表示买家授权完成后,由 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"
  }
}

常见问题

问:授权成功后多久需要发起请款?

答:授权成功后最迟 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 = CAPTUREtransactionRequestId 与当前使用的 captureRequestId 完全匹配的交易,则其对应的 transactionResult.resultStatus 为请款结果。
  • paymentAmount:用于核对支付金额。
  • paymentId:表示由 Antom 生成的支付订单 ID,用于退款和对账。

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

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