# 退款创建 refund_create


退款创建接口，用于开发者针对某笔已支付成功的订单发起退款，支持全额退款和部分退款。豆包平台基于担保交易模式，退款资金将原路退回至用户支付账户。

## 使用限制

- 调用频次限制：请遵守平台默认 QPS 限制，避免高频调用。
- 仅支付成功的订单可以发起退款。
- 退款总金额不得超过订单实际支付金额。
- order_id 与 out_order_no 需至少传入一个来指定退款对应的原订单。

## 接口说明

- 业务场景：适用于开发者在用户申请退款、商品质量问题、订单取消等场景下，主动向豆包交易系统发起退款。支持整单退款和部分退款，多品订单退款时需指定退款商品明细。
- 前提条件：调用方需已完成应用创建，并通过 get_client_token 接口获取到有效的应用级 AccessToken。对应订单需已支付成功（pay_status 为 SUCCESS）。
- 注意事项：
- order_id 为平台侧订单号，out_order_no 为开发者侧订单号，两者均可用于指定原订单，但建议优先使用 order_id。
- out_refund_no 为开发者自行生成的退款单号，同一 out_refund_no 重复调用不会产生多笔退款，接口具备幂等性。
- 多品订单退款时需通过 refund_sku_list 指定各商品的退款金额。

## 基本信息

| 名称 | 描述 |
|-|-|
| HTTP Path | /api/trade_basic/v1/developer/refund_create |
| HTTP Method | POST |
| 权限要求 | - 需要申请权限 - 申请路径：智能服务开发者平台控制台 > 应用详情 > 能力 > 支付权限 - 需要应用级 AccessToken |

## 请求头

| 名称 | 类型 | 必填 | 描述 |
|-|-|-|-|
| content-type | string | 是 | 固定值 "application/json" |
| x-DB-AccessToken | string | 是 | 调用 get_client_token 接口生成的应用级访问凭证 |

## 请求参数

### Body

| 名称 | 类型 | 是否必填 | 描述 | 示例值 |
|-|-|-|-|-|
| order_id | string | 否（与 out_order_no 二选一） | 交易订单号，order_id 与 out_order_no 二选一 | 70214322040407612\*\*\* |
| out_order_no | string | 否（与 order_id 二选一） | 开发者侧订单号，order_id 与 out_order_no 二选一，长度不超过 64 byte | ext_order_123 |
| out_refund_no | string | 是 | 开发者侧退款单号，长度不超过 64 byte | ext_refund_1 |
| refund_reason | string | 是 | 退款原因 | 用户申请退款 |
| refund_total_amount | string | 是 | 退款总金额，单位：分 | 1000 |
| refund_sku_list | RefundSku[] | 否 | 多品订单退款时需要填写，指定各商品的退款金额 | 见下方 RefundSku 结构说明 |
| notify_url | string | 否 | 退款结果回调地址，必须是 HTTPS 类型 |  |

### RefundSku 结构

| 名称 | 类型 | 是否必填 | 描述 | 示例值 |
|-|-|-|-|-|
| sku_id | string | 是 | 下单时的外部商品 ID，需与下单时一致 | ext_sku_1 |
| refund_amount | string | 是 | 该商品退款金额，单位：分 | 1000 |

## 请求示例

cURL

```shell
curl -X POST 'https://api.doubao-dev.com/api/trade_basic/v1/developer/refund_create' \
  -H 'content-type: application/json' \
  -H 'x-DB-AccessToken: clt.943da17996fb5cebfbc70c044c3fc25a57T54DcjT6HNKGqn******' \
  -d '{
    "order_id": "70214322040407612***",
    "out_refund_no": "ext_refund_1",
    "refund_reason": "用户申请退款",
    "refund_total_amount": "1000",
    "refund_sku_list": [
        {
            "sku_id": "ext_sku_1",
            "refund_amount": "1000"
        }
    ],
    "notify_url": "https://example.com/refund/callback"
  }'
```

Go

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

type RefundSku struct {
    SkuId        string `json:"sku_id"`
    RefundAmount string `json:"refund_amount"`
}

type RefundCreateRequest struct {
    OrderId            string      `json:"order_id"`
    OutRefundNo        string      `json:"out_refund_no"`
    RefundReason       string      `json:"refund_reason"`
    RefundTotalAmount  string      `json:"refund_total_amount"`
    RefundSkuList      []RefundSku `json:"refund_sku_list"`
    NotifyUrl          string      `json:"notify_url"`
}

func main() {
    url := "https://api.doubao-dev.com/api/trade_basic/v1/developer/refund_create"
    reqBody := RefundCreateRequest{
        OrderId:           "70214322040407612***",
        OutRefundNo:       "ext_refund_1",
        RefundReason:      "用户申请退款",
        RefundTotalAmount: "1000",
        RefundSkuList: []RefundSku{
            {SkuId: "ext_sku_1", RefundAmount: "1000"},
        },
        NotifyUrl: "https://example.com/refund/callback",
    }
    jsonData, _ := json.Marshal(reqBody)

    req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
    req.Header.Set("content-type", "application/json")
    req.Header.Set("x-DB-AccessToken", "clt.943da17996fb5cebfbc70c044c3fc25a57T54DcjT6HNKGqn******")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
```

Java

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class RefundCreate {
    public static void main(String[] args) throws Exception {
        String url = "https://api.doubao-dev.com/api/trade_basic/v1/developer/refund_create";
        String requestBody = "{"
            + "\"order_id\":\"70214322040407612***\","
            + "\"out_refund_no\":\"ext_refund_1\","
            + "\"refund_reason\":\"用户申请退款\","
            + "\"refund_total_amount\":\"1000\","
            + "\"refund_sku_list\":[{\"sku_id\":\"ext_sku_1\",\"refund_amount\":\"1000\"}],"
            + "\"notify_url\":\"https://example.com/refund/callback\""
            + "}";

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("content-type", "application/json")
                .header("x-DB-AccessToken", "clt.943da17996fb5cebfbc70c044c3fc25a57T54DcjT6HNKGqn******")
                .POST(HttpRequest.BodyPublishers.ofString(requestBody))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}
```

Node.js

```javascript
const axios = require('axios');

async function refundCreate() {
    const url = 'https://api.doubao-dev.com/api/trade_basic/v1/developer/refund_create';
    const headers = {
        'content-type': 'application/json',
        'x-DB-AccessToken': 'clt.943da17996fb5cebfbc70c044c3fc25a57T54DcjT6HNKGqn******'
    };
    const data = {
        order_id: '70214322040407612***',
        out_refund_no: 'ext_refund_1',
        refund_reason: '用户申请退款',
        refund_total_amount: '1000',
        refund_sku_list: [
            { sku_id: 'ext_sku_1', refund_amount: '1000' }
        ],
        notify_url: 'https://example.com/refund/callback'
    };

    try {
        const response = await axios.post(url, data, { headers });
        console.log(response.data);
    } catch (error) {
        console.error('Error:', error.message);
    }
}

refundCreate();
```

## 响应参数

| 名称 | 类型 | 是否必填 | 描述 | 示例值 |
|-|-|-|-|-|
| code | int32 | 是 | 错误码，0 表示成功，非 0 表示失败 | 0 |
| msg | string | 是 | 错误提示 | success |
| log_id | string | 是 | 请求日志 ID，用于问题排查时提供给技术支持 | 2022092115392201020812109511046 |
| data | object | 否 | 退款结果对象，仅在创建成功时返回 | 见下方 data 字段说明 |

### data 字段说明

| 名称 | 类型 | 是否必填 | 描述 | 示例值 |
|-|-|-|-|-|
| refund_id | string | 是 | 交易系统的退款 ID，与 out_refund_no 一一对应 | 80214322040407612\*\*\* |

## 响应示例

### 正常示例

```json
{
    "code": 0,
    "msg": "success",
    "log_id": "2022092115392201020812109511046",
    "data": {
        "refund_id": "80214322040407612***"
    }
}
```

### 异常示例

```json
{
    "code": 500020003,
    "msg": "out_refund_no already exist",
    "log_id": "2022092115392201020812109511047",
    "data": null
}
```

## 错误码

### 通用错误码（模块编号 00）

| 错误码 | 错误名称 | 描述（msg） | 排查建议 |
|-|-|-|-|
| 500000000 | OApiCommonInternalError | internal error | 系统内部错误，请稍后重试；若持续出现请携带 log_id 联系技术支持 |
| 500000001 | OApiCommonDeveloperParamError | Parameter error | 请求参数错误，请检查必填参数是否完整、金额格式是否正确、order_id 和 out_order_no 是否至少传入一个 |
| 500000002 | OApiCommonDeveloperRateLimitError | Rate limit error | 请求触发限流，请降低调用频率后重试 |

### 交易模块错误码

| 错误码 | 错误名称 | 描述（msg） | 排查建议 |
|-|-|-|-|
| 500020003 | OApiTradeOutRefundIDExistError | out_refund_no already exist | 退款单号已存在，请确认是否重复提交；若需查询退款状态请使用 refund_query 接口 |
| 500020005 | OApiTradeOrderNotFoundError | Order not found | 订单不存在，请确认 order_id 或 out_order_no 是否正确，订单是否已成功创建并支付 |
