退款

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

退款规则

退款的相关规则如下:

  • 退款限制:不同的支付方式存在不同的限制差异,具体包括以下几种情况:
    • 不支持退款
    • 不支持部分退款
    • 从支付成功的时间起,超出一定时间后不允许退款

各支付方式的退款能力及退款周期详见支持的支付方式

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

退款方式

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

  • 调用接口:您可以通过调用 退款 接口对成功支付的交易发起退款。
  • 使用 Antom Dashboard 进行人工退款:关于如何发起退款并查看退款结果,请参阅退款

退款流程

以下是退款的流程图:

退款.png

  1. 调用 退款 接口。
  2. 处理退款响应

根据 退款 接口返回的结果进行相应处理。

  1. 获取退款结果。

通过以下两种方法获取退款结果:

    • 异步通知:在 退款 接口中指定 refundNotifyUrl 字段,以接收退款结果的异步通知,或者在 Antom Dashboard 中指定接收通知的 webhook URL。当退款成功或失败时,Antom 会使用 退款通知 接口向您发送异步通知。
    • 同步查询:使用 退款查询 接口获取最终的退款结果。

退款步骤

根据以下步骤开始集成:

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

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

以下是调用 退款 接口的示例代码:

copy
public static void payRefund() {
        AlipayPayRefundRequest alipayPayRefundRequest = new AlipayPayRefundRequest();

        // 替换为您的 refundRequestId
        String refundRequestId = UUID.randomUUID().toString();
        alipayPayRefundRequest.setRefundRequestId(refundRequestId);

        // 替换为您的 paymentId
        alipayPayRefundRequest.setPaymentId("yourPaymentId");

        // 设置金额
        // 您需要转换金额单位(在实际操作中,金额应该在您的服务端进行计算)
        Amount amount = Amount.builder().currency("USD").value("100").build();
        alipayPayRefundRequest.setRefundAmount(amount);

        // 替换为您的 refundReason
        alipayPayRefundRequest.setRefundReason("yourRefundReason");

        // 替换为您的 refundNotifyUrl
        alipayPayRefundRequest.setRefundNotifyUrl("yourRefundNotifyUrl");

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

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

错误码

说明

ORDER_STATUS_INVALID

支付未成功。

REFUND_WINDOW_EXCEED

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

PAYMENT_METHOD_NOT_SUPPORTED

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

PARTIAL_REFUND_NOT_SUPPORTED

此交易不支持部分退款。

MERCHANT_BALANCE_NOT_ENOUGH

账户余额不足。

以下是发起 退款 请求的关键参数:

参数名称

是否必需

描述

refundRequestId

商户端唯一的退款 ID。

paymentId

该笔退款所对应的 Antom 分配的原始交易的 ID。此参数的值需和 支付(代扣)接口中的 paymentId 保持一致。

refundAmount

退款金额。其包含以下参数:

  • currency退款币种。此参数的值需和 支付(代扣)接口中的支付金额币种(paymentAmount.currency)保持一致。
  • value:退款金额(以最小币种单位的正整数形式表示)。单笔退款金额需小于等 支付(代扣)接口中的支付金额(paymentAmount.value),同时多次部分退款的总金额也需小于等于 支付(代扣)接口中此 paymentId 的总金额。

refundNotifyUrl

Antom 向您发送退款异步通知的地址。

以下是发起 退款 请求的代码示例:

copy
{
  "paymentId": "20181129190741010007000000XXXX",
  "refundReason": "amsdemorefund",
  "refundRequestId": "20181129190741020007000000XXXX",
  "refundAmount": {
    "currency": "USD",
    "value": "100"
  }
}

在您调用 退款 接口后,Antom 会返回该次退款的受理结果。以下是退款响应的代码示例:

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

响应中 result.resultStatus 字段的值代表该笔交易的退款状态,请您根据指引进行处理:

result.resultStatus

信息

后续操作

S

退款成功。

无需进一步操作。

U

交易处理中。

其包含以下情况:

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

F

退款失败。

建议更换 refundRequestId 重试如果问题未解决,联系 Antom 技术支持排查问题。

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

Antom 会通过服务器交互将相应的退款结果发送给您,您可选择以下方法之一获取退款结果:

  • 接收来自 Antom 的异步通知
  • 主动查询退款结果

接收退款通知

完成以下操作以接收来自 Antom 的 退款通知

1. 设置接收通知的 webhook URL

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

  • 推荐 若您的每个订单都有单独的通知 URL,建议您在每笔请求中设置 webhook URL。您可以在 退款 接口请求的refundNotifyUrl 参数传入该笔订单的异步通知接收 URL。
  • 若您的所有订单有统一的通知 URL,您可以在 Antom Dashboard > 开发者 >通知地址 中设置 webhook URL。具体操作请参阅通知地址

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

copy
{
    "notifyType": "REFUND_RESULT",
    "refundAmount": {
        "currency": "USD",
        "value": "100"
    },
    "refundId": "40181129190741020007000000XXXX",
    "refundRequestId": "20181129190741020007000000XXXX",
    "refundStatus": "SUCCESS",
    "refundTime": "2020-10-10T12:01:01+08:30",
    "result": {
        "resultCode": "SUCCESS",
        "resultMessage": "success.",
        "resultStatus": "S"
    }
}

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

result.resultStatus

信息

后续操作

S

退款成功。

无需进一步操作。

F

退款失败。

建议更换 refundRequestId 重试如果问题未解决,联系 Antom 技术支持排查问题。

注意:发起退款后,Antom 默认向您异步通知。但部分失败场景如参数异常时,退款 接口响应会同步返回 F,此时 Antom 不会发送异步通知,可直接视为退款失败。

2. 异步通知验签

若您收到 Antom 的异步通知,需要您在返回中按照示例代码格式返回响应,但无需做加签处理。

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

copy
/**
 * receive notify
 *
 * @param request    request
 * @param notifyBody notify body
 * @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())) {
                // 处理您自身的业务逻辑
                // 例如,支付信息和买家之间的关系保存在数据库中
                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();
}

无论订单是否退款成功,每个通知请求均需按以下固定格式响应:

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

查询退款结果

除了通过异步通知获取退款结果,您也可以通过 退款查询 接口主动查询退款结果,使用退款请求中的 refundRequestId 查询支付状态。

参数名称

是否必需

描述

refundRequestId

商户为识别退款请求而分配的特定 ID。

以下为调用 退款查询 接口的代码示例:

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

    // 替换为您的 refundId
    alipayInquiryRefundRequest.setRefundId("yourRefundId");

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

以下为 退款查询 响应的代码示例:

copy
{
  "refundAmount": {
    "currency": "USD",
    "value": "100"
  },
  "refundId": "40181129190741020007000000XXXX",
  "refundRequestId": "20181129190741020007000000XXXX",
  "refundStatus": "SUCCESS",
  "refundTime": "2020-10-10T12:01:01+08:30",
  "result": {
    "resultCode": "SUCCESS",
    "resultMessage": "success.",
    "resultStatus": "S"
  }
}

下表展示了退款查询的响应中 refundStatus 可能返回的值,请您根据指引进行处理:

refundStatus

信息

后续操作

SUCCESS

退款成功。

无需进一步操作。

FAIL

退款失败。

建议更换 refundRequestId 重试如果问题未解决,联系 Antom 技术支持排查问题。

PROCESSING

退款处理中。

建议继续查询,直到获取终态结果或收到退款异步通知。

常见问题

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

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

问:退款成功后,买家多久能收到退款到账?

答:退款成功代表支付方式侧已受理买家的退款请求,资金到账的具体时间取决于支付方式侧。