订单退款接口

更新时间:2026-07-04  ·  服务名称:order.refund.api

接口说明

交易退款接口用于对已支付成功的订单发起退款操作,支持全额退款部分退款。系统支持主流支付渠道的退款功能,确保商户能够灵活处理各种退款需求。

适用对象
直连商户、间连商户
请求 URL
https://api.example.com/openapi/v1/refund/create
请求方法
POST

退款流程图

退款流程图

请求参数(bizContent)

参数名类型必填说明
refundNoString Y 退款订单号,必须唯一,用于幂等控制
transactionNoString C 平台订单号(与商户订单号必需填一个)
orderNoString C 商户订单号(与平台订单号必需填一个)
refundAmountString Y 申请退款金额,单位元,需保留小数点后两位;示例值:100.00,最低传入 0.01
notifyUrlString N 异步通知地址,示例值:http://service.example.com/to/path
remarkString N 备注,原样返回;示例值:测试退款
⚠️
注意事项:
  • 退款订单号(refundNo)必须唯一,避免重复退款
  • 退款金额(refundAmount)不能超过原订单可退款金额
  • 对于部分退款,多次退款金额总和不能超过原订单金额

响应参数

公共返回参数

参数名类型必填说明
codeString Y 响应状态码,成功为 "0000"
messageString Y 响应描述
timestampString Y 响应时间戳,UTC 时间,ISO 8601 格式
dataObject N 业务数据,无数据时返回空对象 {}

响应附加参数(data)

参数名类型必填说明
refundTransactionNoStringY平台退款订单号
transactionNoStringY平台订单号
refundAmountNumberY本次退款金额,单位元
totalRefundedNumberY累计退款金额,单位元
isFullRefundBooleanY是否全额退款
statusIntegerY退款状态(见下表)

退款状态码说明

状态码状态说明
0退款中退款请求已提交,等待处理
1退款成功退款已成功到账
2退款失败退款处理失败

请求示例

退款示例

JSON
{
  "refundNo": "REFUND202607041659484287",
  "transactionNo": "SHPAY202607041623307050108",
  "orderNo": "TESTPAY202607041623307050108",
 "refundAmount": "100.00",
  "notifyUrl": "",
  "remark": "测试退款-165948"
}

响应示例

退款成功示例

JSON
{
  "code": "0000",
  "message": "success",
  "timestamp": "2026-07-04T09:56:49.000Z",
  "data": {
    "refundTransactionNo": "SHRF20260704175649658133",
    "transactionNo": "SHPAY202607041623307050108",
    "refundAmount": 100,
    "totalRefunded": 100,
    "isFullRefund": false,
    "status": 0
  }
}

退款失败示例

JSON
// 退款金额超限
{
  "code": "4001",
  "message": "退款金额超过可退款金额",
  "timestamp": "2026-07-04T09:56:49.000Z",
  "data": {}
}

// 订单不存在
{
  "code": "4004",
  "message": "原订单不存在",
  "timestamp": "2026-07-04T09:56:49.000Z",
  "data": {}
}

退款异步通知

系统支持退款异步通知功能,可以在完成退款处理后向商户系统发送通知。

通知参数

参数名类型说明
transactionNoString平台订单号
refundTransactionNoString退款交易号
refundAmountNumber退款金额
totalRefundedNumber累计退款金额
isFullRefundBoolean是否全额退款
statusInteger退款状态:0=成功,1=失败,2=提交成功,3=未知
signString签名

通知机制

ℹ️
通知规则:
  • 退款请求提交后,系统会根据退款状态的变化主动向商户发送异步通知
  • 异步通知采用 POST 方式,商户需确保通知地址可正常接收
  • 商户接收到通知后,返回字符串 "SUCCESS" 表示已收到通知,否则系统会按一定频率重复发送通知

通知时机

  • status=0 退款成功时发送通知
  • status=1 退款失败时发送通知
  • 状态变更 某些渠道会在退款状态变更时发送通知

签名验证

为确保通知的真实性,商户应该验证签名:

  1. 将除 sign 参数外的所有参数按照字母顺序排序
  2. 拼接成 key=value&key=value 的形式
  3. 在拼接后的字符串末尾加上商户密钥
  4. 使用对应的签名算法(MD5 或 RSA2)计算签名
  5. 将计算得到的签名与通知中的 sign 参数比对

代码示例

Java
import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;

public class OrderRefundExample {

    public static void main(String[] args) {
        // 退款示例
        Map<String, String> refundParams = new HashMap<>();
        refundParams.put("refundNo", "REFUND" + System.currentTimeMillis());
        refundParams.put("transactionNo", "SHPAY202607041623307050108");
        refundParams.put("refundAmount", "100.00");
        refundParams.put("notifyUrl", "https://your-domain.com/refund-notify/");
        refundParams.put("remark", "测试退款");

        String result = submitRefund(refundParams);
        System.out.println("退款结果:" + result);
    }

    public static String submitRefund(Map<String, String> refundParams) {
        String openUserid = "your_userId";
        String apiKey = "your_api_key";
        String apiUrl = "https://openapi.example.com";

        Map<String, String> requestData = new HashMap<>();
        requestData.put("userId", openUserid);
        requestData.put("service", "order.refund.api");
        requestData.put("bizContent", JSON.toJSONString(refundParams));

        String sign = SignUtil.generateSign(requestData, apiKey);
        requestData.put("sign", sign);

        String response = HttpUtil.post(apiUrl, requestData);
        return response;
    }
    
    /** 处理退款异步通知 */
    public static String handleRefundNotify(Map<String, String> notifyParams) {
        String apiKey = "your_api_key";
        String sign = notifyParams.remove("sign");
        String calculatedSign = SignUtil.generateSign(notifyParams, apiKey);

        if (!sign.equals(calculatedSign)) {
            return "FAIL";
        }

        String status = notifyParams.get("status");
        if ("0".equals(status)) {
            System.out.println("退款成功");
        } else if ("1".equals(status)) {
            System.out.println("退款失败");
        }
        return "SUCCESS";
    }
}
Python
import requests
import json
import hashlib
import time

def submit_refund(refund_params):
    userId = "your_userId"
    api_key = "your_api_key"
    api_url = "https://openapi.example.com"
    
    request_data = {
        "userId": userId,
        "service": "order.refund.api",
        "bizContent": json.dumps(refund_params)
    }
    
    sign = generate_sign(request_data, api_key)
    request_data["sign"] = sign
    
    try:
        response = requests.post(api_url, json=request_data, timeout=30)
        return response.json()
    except Exception as e:
        print(f"退款请求失败: {e}")
        return None

def generate_sign(params, api_key):
    sorted_params = sorted(params.items())
    sign_str = "&".join([f"{k}={v}" for k, v in sorted_params if v])
    sign_str += f"&key={api_key}"
    return hashlib.md5(sign_str.encode()).hexdigest().upper()

def handle_refund_notify(notify_params, api_key):
    sign = notify_params.pop('sign', '')
    calculated_sign = generate_sign(notify_params, api_key)
    if sign != calculated_sign:
        return 'FAIL'

    status = notify_params.get('status')
    if status == '0':
        print("退款成功")
    elif status == '1':
        print("退款失败")
    return 'SUCCESS'

# 退款示例
result = submit_refund({
    "refundNo": f"REFUND{int(time.time())}",
    "transactionNo": "SHPAY202607041623307050108",
    "refundAmount": "100.00",
    "notifyUrl": "https://your-domain.com/refund-notify/",
    "remark": "测试退款"
})
PHP
<?php

class OrderRefund {
    private $openUserid = 'your_userId';
    private $apiKey = 'your_api_key';
    private $apiUrl = 'https://openapi.example.com';
    
    public function submitRefund($refundParams) {
        $requestData = [
            'userId' => $this->openUserid,
            'service' => 'order.refund.api',
            'bizContent' => json_encode($refundParams)
        ];
        $requestData['sign'] = $this->generateSign($requestData);
        $response = $this->httpPost($this->apiUrl, $requestData);
        return json_decode($response, true);
    }
    
    public function handleRefundNotify($notifyParams) {
        $sign = $notifyParams['sign'];
        unset($notifyParams['sign']);
        $calculatedSign = $this->generateSign($notifyParams);

        if ($sign !== $calculatedSign) { return 'FAIL'; }

        if ($notifyParams['status'] === '0') {
            // 退款成功
        } elseif ($notifyParams['status'] === '1') {
            // 退款失败
        }
        return 'SUCCESS';
    }
    
    private function generateSign($params) {
        ksort($params);
        $signStr = '';
        foreach ($params as $key => $value) {
            if ($value !== '') { $signStr .= "{$key}={$value}&"; }
        }
        $signStr .= "key={$this->apiKey}";
        return strtoupper(md5($signStr));
    }
    
    private function httpPost($url, $data) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }
}

// 退款示例
$refund = new OrderRefund();
$result = $refund->submitRefund([
    'refundNo' => 'REFUND' . time(),
    'transactionNo' => 'SHPAY202607041623307050108',
    'refundAmount' => '100.00',
    'notifyUrl' => 'https://your-domain.com/refund-notify/',
    'remark' => '测试退款'
]);
print_r($result);

注意事项

🚫
重要提醒:
  • 退款订单号(refundNo)必须保证唯一性,重复的退款订单号会导致退款失败
  • 退款金额不能超过原订单的可退款金额
  • 部分退款时,需要确保多次退款总金额不超过原订单金额
  • 退款结果以异步通知为准,同步返回结果仅表示退款请求已提交
  • 一笔退款失败后重新提交,请不要更换退款单号,请使用原退款单号
⚠️
退款限制:
  • 微信支付:订单支付成功后 360 天内可发起退款
  • 支付宝:订单支付成功后 360 天内可发起退款
  • 银联二维码:订单支付成功后 360 天内可发起退款
  • 抖音支付:交易时间超过一年的订单无法提交退款
ℹ️
多次退款规则:
  • 同一笔订单多次退款的请求需相隔 1 分钟
  • 支持单笔交易分多次退款,每个支付订单的部分退款次数不能超过 50 次
  • 多次退款需要提交原支付订单的平台订单号(transactionNo),并设置不同的退款单号(refundNo
  • 申请退款总金额不能超过订单金额
  • 如果同一个用户有多笔退款,建议分不同批次进行退款,避免并发退款导致退款失败
频率限制:
  • 错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过 6 次
最佳实践:
  • 建议在业务层面记录退款请求流水号,便于后续查询和对账
  • 接收到异步通知后,先验证签名再处理业务逻辑
  • 处理完成后及时返回 SUCCESS,避免重复接收通知
  • 申请退款接口的返回仅代表业务的受理情况,具体退款是否成功,需要通过退款查询接口获取结果