退款

对于已支付成功的订单,如果您需要向买家发起退款,可以选择以下两种方式:

有关退款的具体规则(如手续费、退款结算汇率、退款有效期限等),请以各收单机构的实际约定为准。

注意

针对 Antom 收单机构:

  • 支付方式的具体信息详见支付方式能力
  • 退款返回:指在极端情况下,由于买家在银行侧的账户状态不正常等原因,导致发生调用 退款 接口后返回退款成功,但是买家未能成功收到资金的情况。此时,Antom 会结算相应的资金,并通过结算账单的方式发送通知,需要您自行决定如何处理这一笔资金。

通过 APO Dashboard 退款

您可以通过 APO Dashboard 发起退款并查看退款结果

调用退款接口退款

退款的流程请参考下图:

退款中文.png

图 1. 退款流程

退款步骤

根据以下步骤开始集成:

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

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

使用 退款 接口发起退款需满足以下要求,否则将收到 APO 返回的对应错误码:

退款信息

要求

对应错误码

退款的币种

退款请求中的币种 refundAmount.currency 需要与发起交易时的币种 paymentAmount.currency 保持一致。

CURRENCY_NOT_SUPPORT

退款金额

退款申请的金额 refundAmount.value 需要大于等于最小退款金额(通常与最小支付金额一致),且小于等于交易剩余可退金额。

REFUND_AMOUNT_EXCEED

交易状态

对于卡支付方式:

  • 仅请款成功的交易才能支持退款;
  • 在卡拒付场景下,处于争议处理阶段的交易无法发起退款;仅当拒付判责完成后允许发起退款。

ORDER_STATUS_INVALID

退款期限

在支付方式支持的退款期限内允许发起退款,超过则不允许发起退款。

REFUND_WINDOW_EXCEED

表 1. 错误码

以下示例代码展示了如何调用 退款 接口:

copy
public static void refund() {
    AlipayRefundRequest alipayRefundRequest = new AlipayRefundRequest();
    // 替换为您的 refundRequestId
    String refundRequestId = UUID.randomUUID().toString();
    alipayRefundRequest.setRefundRequestId(refundRequestId);
    alipayRefundRequest.setPaymentId("202505151940108001001889A0235440943");
    alipayRefundRequest.setRefundReason("demo refund");
alipayRefundRequest.setRefundNotifyUrl("https://kademo.intlalipay.cn/payments/notifySuccess");
    // 设置退款金额
    Amount amount = Amount.builder().currency("USD").value("1000").build();
    alipayRefundRequest.setRefundAmount(amount);

    AlipayRefundResponse alipayRefundResponse;
    try {
        alipayRefundResponse = CLIENT.execute(alipayRefundRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // 处理错误情况
    }
}

退款请求中关键字段如下(具体内容请参见 退款 接口):

参数

是否必填

说明

refundRequestId

在商户侧唯一的退款 ID。

paymentId

该笔退款所对应的 APO 分配的原始交易的 ID。

refundAmount.value

退款金额。需要大于等于最小退款金额(通常为最小支付金额),且小于等于交易剩余可退金额。

refundAmount.currency退款币种。需和 支付 接口中的支付金额的币种(paymentAmount.currency)保持一致。

refundNotifyUrl

APO 向您发送退款异步通知地址。如果您希望接收退款结果的异步通知,请指定此字段。如果请求和 APO Dashboard 中都指定了退款通知链接,则请求中指定的值优先。

表 2. 退款请求的关键参数

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

copy
{
    "paymentId": "20241212194010800100188670211082739",
    "refundReason": "amsdemorefund",
    "refundRequestId": "yuqian_refund_654ac17e-bc5e-4648-b9de-a18f0a74aa2a",
    "refundAmount": {
        "currency": "SGD",
        "value": "1000"
    },
  "refundNotifyUrl":"https://kademo.intlalipay.cn/payments/notifySuccess"
}

以下代码展示了一个响应的示例,其中包含以下参数:

copy
{
    "acquirerInfo": {
        "acquirerMerchantId": "764764000015445",
        "acquirerName": "2C2P",
        "acquirerTransactionId": "750130125",
        "referenceRequestId": "2025043019031303099320289169177"
    },
    "acquirerReferenceNo": "2025043019031300010320289179466",
    "paymentId": "20250430194010890100111320263308754",
    "refundAmount": {
        "currency": "USD",
        "value": "110"
    },
    "refundId": "20250430194010890100111320263188180",
    "refundRequestId": "REFUND_20250430152656859_AUTO",
    "refundTime": "2025-04-30T00:26:58-07:00",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

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

result.resultStatus

信息下一步操作
S表示退款成功。

无需进一步操作。

F

表示退款失败。

请根据错误吗确认原因再决定是否要发起重试。如果需要重试,请您更换 refundRequestId 再次重试。

U

表示退款处理中。

可能存在以下两种情况:

  • REFUND_IN_PROCESS:表示退款正在受理中,您可选择主动调用 退款查询 接口获取退款结果,或者等待退款结果通知。
  • UNKNOWN_EXCEPTION/REQUEST_TRAFFIC_EXCEED_LIMIT:这种情况一般是 APO 系统或者网络原因导致,您可以使用原 refundRequestId 调用 退款 接口进行重试。

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

退款发起后,您可以通过以下方法之一获取支付结果:

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

设置异步通知

1. 设置接收通知的 webhook URL

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

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

注意如果请求和 APO Dashboard 中都指定了退款通知链接,则请求中指定的值优先。

以下代码展示了通知请求的示例:

copy
{
    "notifyType": "REFUND_RESULT",
    "paymentId": "20250430194010890100111320263308754",
    "paymentRequestId": "PAYMENT_20250430151543077_AUTO",
    "refundAmount": {
        "currency": "USD",
        "value": "110"
    },
    "refundId": "20250430194010890100111320263188180",
    "refundRequestId": "REFUND_20250430152656859_AUTO",
    "refundStatus": "SUCCESS",
    "refundTime": "2025-04-30T00:26:58-07:00",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

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

result.resultStatus

信息

下一步操作

S

表示退款成功。

无需进一步操作。

F

表示退款失败。

您可更换 refundRequestId 再次重试或联系 APO 技术支持。

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("REFUND_RESULT".equals(notifyType)){
            AlipayRefundNotify paymentNotify = jsonObject.toJavaObject(AlipayRefundNotify.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();
            }
        }
        // other types of notifications
        
    } 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"
    }
}

查询退款

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

以下代码展示了如何调用 退款查询 接口:

copy
public static void inquiryRefund() {
    AlipayInquiryRefundRequest alipayInquiryRefundRequest = new AlipayInquiryRefundRequest();

    // replace with your refundId
    alipayInquiryRefundRequest.setRefundId("yourRefundId");

    AlipayInquiryRefundResponse alipayInquiryRefundResponse = null;
    try {
        alipayInquiryRefundResponse = CLIENT.execute(alipayInquiryRefundRequest);
    } catch (AlipayApiException e) {
        String errorMsg = e.getMessage();
        // handle error condition
    }
}

以下代码展示了一个响应报文的示例:

copy
{
    "acquirerInfo": {
        "acquirerMerchantId": "764764000015445",
        "acquirerName": "2C2P",
        "acquirerTransactionId": "750130125",
        "referenceRequestId": "2025043019031303099320289169177"
    },
    "refundAmount": {
        "currency": "USD",
        "value": "110"
    },
    "refundId": "20250430194010890100111320263188180",
    "refundRequestId": "REFUND_20250430152656859_AUTO",
    "refundStatus": "SUCCESS",
    "refundTime": "2025-04-30T00:26:58-07:00",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

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

refundStatus

信息

下一步操作

SUCCESS

表示退款成功。

无需进一步操作。

FAIL

表示退款失败。

请您更换 refundRequestId 再次重试或联系 APO 技术支持。

PROCESSING表示退款正在进行。

请您继续查询退款,直到拿到终态结果或收到退款异步通知。

常见问题

Q:发起退款是会立刻返回退款结果吗?

A:并非所有支付方式可以同步返回终态退款结果,所以请以异步通知的退款结果为准。

Q:退款成功后买家多久会到账?

A:具体到账时间以发卡侧为准。