退款

您可以通过本文了解 Antom 的退款规则及如何对成功的交易发起退款。

退款规则

退款的相关规则如下:

  • 手续费:退款是否退回支付手续费,以及退款是否额外收费,依照双方合约执行。
  • 退款结算汇率:当结算币种和支付币种不一致时,退款结算汇率按照发起退款请求当日 Antom 汇率进行结算。
  • 退款限制:不同的支付方式存在不同的限制差异,具体包括以下几种情况:
    • 不支持退款
    • 不支持部分退款
    • 从支付成功的时间起,超出一定时间后不允许退款

以上具体信息详见支持的支付方式

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

退款方式

在支付成功后,您可以通过以下两种方法发起退款:

  • 调用接口:通过调用 refund 接口对成功支付的交易发起退款。
  • 人工处理联系运营人员通过 Antom Dashboard 进行退款。

退款流程

下图为退款流程图:

退款.png

注意

  • 调用 refund 接口后,可能返回以下三种情况:
    • 退款请求返回退款受理成功:此时结果代码返回 REFUND_IN_PROCESS,请使用 inquiryRefund 接口获取最终的退款结果,或者等待退款结果通知。
    • 退款请求返回退款失败:如果请求参数错误,请根据返回的错误码信息尝试调整退款的参数并换号重试,其他情况可联系 Antom 技术支持寻求帮助。各错误码建议处理方式详见错误代码
    • 退款请求返回退款未知异常:此时一般为网络原因,可尝试不换号重试。
  • 退款通知:如果退款成功则一定会发送退款通知。在退款失败的情况下,如果是退款请求非法导致受理异常,则不会有退款通知。

集成退款

根据以下步骤开始集成:

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

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

根据支付方式的不同,调用接口退款时退款可能是同步或者是异步发生的,因此退款分为同步退款和异步退款两种逻辑

  • 同步退款:调用 refund 接口后立即返回最终状态(成功/失败)。

注意:极少数情况下可能因系统或支付渠道异常返回其他状态。

  • 异步退款:调用 refund 接口返回处理中状态。需通过以下方式获取最终结果:
    • 接收异步结果通知
    • 主动查询退款状态

同步和异步退款所支持的支付方式信息如下:

同步退款

异步退款

Alipay+ 类型的所有支付方式

非同步退款支持的其它支付方式均为异步退款,所有支付方式请参考支持的支付方式

收单机构为 2C2P HK 和 2C2P SG 时的 Mastercard 和 Visa 卡

表 2. 同步和异步退款支持的支付方式

调用 refund 接口以下是请求报文重点字段:

类型

字段

是否必填

字段描述

基础字段

refundRequestId

商户端唯一的退款 ID。

refundAmount.currency

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

refundAmount.value

退款金额。按币种的最小单位设置,单笔金额需小于等于

notifyPayment.request.paymentAmount 金额,同时多次退款总金额也需小于等于该次请款总金额。

paymentId

支付异步通知返回的 Antom 单号

表 3. 请求报文重点字段

调用 refund 接口代码示例

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");

    // 设置退款金额
    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();
        // 处理错误情况
    }
}

退款请求代码示例:

copy
{
    "paymentId": "2024121219401080010018867021108****",
    "refundReason": "amsdemorefund",
    "refundRequestId": "yuqian_refund_654ac17e-bc5e-4648-b9de-a18f0a74****",
    "refundAmount": {
        "currency": "SGD",
        "value": "1000"
    }
}

退款响应代码示例:

copy
{
  "result": {
    "resultCode": "SUCCESS",
    "resultStatus": "S",
    "resultMessage": "Success"
  },
  "refundAmount": {
    "value": "100",
    "currency": "USD"
  },
  "refundTime": "2020-10-10T12:01:01+08:30",
  "paymentId": "20181129190741010007000000****",
  "refundRequestId": "20181129190741020007000000****",
  "refundId": "40181129190741020007000000****"
}

根据响应中 result.resultStatus 的值进行处理:

result.resultStatus

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

无需进一步操作。

F

表示退款失败。

更换 refundRequestId 再次重试或联系 Antom 技术支持。

U

表示由于未知原因退款失败。

可能存在以下两种情况:

  • REFUND_IN_PROCESS:表示退款正在受理中,您可选择主动调用 inquiryRefund 接口获取退款结果,或者等待退款结果通知。
  • 其他错误码:建议使用原请求调用 refund 接口进行重试。

表 4. 错误码

调用接口发起退款时若不满足以下退款要求,将收到 Antom 返回的对应错误码:

状态

错误码

支付未成功。

ORDER_STATUS_INVALID

退款日期超出了退款有效期。

REFUND_WINDOW_EXCEED

支付所使用的支付方式不支持取消交易或退款。

PAYMENT_METHOD_NOT_SUPPORTED

此交易不支持部分退款。

PARTIAL_REFUND_NOT_SUPPORTED

账户余额不足。

MERCHANT_BALANCE_NOT_ENOUGH

表 4. 错误码

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

发起退款后,可通过以下方式获取退款结果:

  • 接收异步通知:接收 Antom 服务端发送的退款结果。
  • 查询退款结果:调用 inquiryRefund 接口获取退款结果。
  1. 设置接收通知的 Webhook URL:

当退款发起后默认都有异步通知,部分失败场景如参数异常等,退款接口同步返回 F,不会发异步通知,可直接认为退款失败。您可以选择以下两种方法中的一种来设置接收通知的 webhook URL:

  • 订单级通知配置 推荐通过 refund 接口请求的 refundNotifyUrl 字段为每笔订单指定独立的通知 URL。
  • 商户级通知配置:登陆 Antom Dashboard > 开发者 > 通知地址,为 alipay.ams.payments.refundnotify 接口增加通知地址。具体操作请参阅 通知地址

以下是退款结果异步通知请求的代码示例:

copy
{
    "notifyType": "REFUND_RESULT",
    "refundAmount": {
        "currency": "USD",
        "value": "1000"
    },
    "refundId": "20241212194010801300188670203854640",
    "refundRequestId": "yuqian_refund_654ac17e-bc5e-4648-b9de-a18f0a74aa2a",
    "refundStatus": "SUCCESS",
    "refundTime": "2024-12-11T23:37:33-08:00",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

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

result.resultStatus

信息

下一步操作

S

表示退款成功。

无需进一步操作。

F

表示退款失败。

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

  1. Antom 发送的通知结果由 Antom 加签,故建议您验证签名以确认通知由 Antom 发送。参考以下代码示例对退款通知进行验签:
copy
/**
 * 接收通知
 *
 * @param request    请求
 * @param notifyBody 通知体
 * @return Result
 */
@PostMapping("/receiveNotify")
@ResponseBody
public Result receiveNotify(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");

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

        // 反序列化通知体
        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();
            }
        }
        // 其他类型的通知
        
    } catch (Exception e) {
        // 处理错误情况
        return Result.builder().resultCode("FAIL").resultMessage("fail.").resultStatus(ResultStatusType.F).build();
    }

    return Result.builder().resultCode("SYSTEM_ERROR").resultMessage("system error.").resultStatus(ResultStatusType.F).build();
}
  1. 收到通知后,您无需对响应通知结果做加签处理,但是对于每个通知请求均需按以下固定格式响应,与订单退款成功与否无关。
copy
{
  "result": {
    "resultCode": "SUCCESS",
    "resultStatus": "S",
    "resultMessage": "success"
  }
}

常见问题

问:发起退款后会立刻返回退款结果吗?

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

问:退款成功后买家多久会收到退款?

答:收到退款成功通知仅代表发卡侧已受理退款请求,退款具体到账时间以发卡侧为准。