> For the complete documentation index, see [llms.txt](https://docs.coda.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.coda.co/https-coda-payments.gitbook.io-zhong-wen-coda-private-technical-documentation/codapay/tui-kuan-api.md).

# 退款 API

本指南全面介绍了 Codapay 的退款 API。文档详细说明了商户如何通过编程方式发起退款、查询退款状态以及处理退款通知。

## 退款场景概览

Codapay 仅支持对已完成支付的订单进行退款操作。若请求退款时支付未完成或已取消，系统将返回错误响应

## 1. 退款集成流程

### 已完成支付的退款处理

<figure><img src="/files/uO1BxJU5p1DFpClloqxe" alt=""><figcaption></figcaption></figure>

### 已取消支付的退款处理

<figure><img src="/files/LjgUiTnUCQhdaCC4tIx8" alt=""><figcaption></figcaption></figure>

## 2. 创建退款

### 接口 URL

```json
POST {domain}/airtime/api/restful/v2.0/Payment/createRefund.json
```

### 示例

```javascript
{
  "refundRequest": {
    "apiKey": "<your_api_key>",
    "txnId": 1234567890,
    "country": "ID",
    "projectId": 100,
    "customerEmail": "optional@example.com"
  }
}
```

### 请求参数

<table><thead><tr><th width="261.60504150390625">参数</th><th width="106.4140625">类型</th><th width="89.2586669921875">必填</th><th>描述</th></tr></thead><tbody><tr><td>refundRequest</td><td>Object</td><td>是</td><td>有效的 JSON 对象</td></tr><tr><td>refundRequest.apiKey</td><td>String</td><td>是</td><td>Coda 提供的 API 密钥</td></tr><tr><td>refundRequest.txnId</td><td>Numeric</td><td>是</td><td>需要退款的交易 ID</td></tr><tr><td>refundRequest.country</td><td>String</td><td>否</td><td>ISO 3166 国家代码</td></tr><tr><td>refundRequest.projectId</td><td>Numeric</td><td>是</td><td>唯一的项目 ID</td></tr><tr><td>refundRequest.customerEmail</td><td>String</td><td>否</td><td>用户邮箱地址</td></tr></tbody></table>

### 响应示例

```javascript
{
  "refundResult": {
    "id": "rfl_sg195468b7c6c0000",
    "createdAt": "2023-03-08T15:30:00.000000000Z",
    "statusCode": "accepted",
    "type": "full",
    "txnId": 1234567890,
    "amountValue": 40000,
    "amountCurrency": "IDR"
  }
}
```

### 响应字段

<table><thead><tr><th width="209.6024169921875">参数</th><th width="94.9852294921875">类型</th><th>说明</th></tr></thead><tbody><tr><td>refundResult</td><td>Object</td><td>JSON 对象</td></tr><tr><td>refundResult.id</td><td>String</td><td>退款 ID (Refund ID)</td></tr><tr><td>refundResult.createdAt</td><td>String</td><td><p>时间戳（ISO 8601 格式）</p><p></p><p>支持格式：<br><br>YYYY-MM-DDThh:mm:ss.ssssssZ（微秒级）<br>YYYY-MM-DDThh:mm:ss.sssssssssZ（纳秒级）</p></td></tr><tr><td>refundResult.statusCode</td><td>String</td><td>状态：accepted、succeeded 或 failed</td></tr><tr><td>refundResult.finalizedAt</td><td>String</td><td>退款完成时的可选时间戳<br><br>支持格式：<br><br>YYYY-MM-DDThh:mm:ss.ssssssZ（微秒级）<br>YYYY-MM-DDThh:mm:ss.sssssssssZ（纳秒级）<br><br>*如为 null，该字段不会返回</td></tr><tr><td>refundResult.type</td><td>String</td><td>退款类型，始终为 "full"</td></tr><tr><td>refundResult.txnId</td><td>Numeric</td><td>已退款的交易 ID</td></tr><tr><td>refundResult.amountValue</td><td>Numeric</td><td>退款金额</td></tr><tr><td>refundResult.amountCurrency</td><td>String</td><td>退款币种</td></tr></tbody></table>

### 错误响应示例

```javascript
{
  "refundResult": {
    "resultCode": 551
    "resultDesc": "Duplicate Refund Request"
  }
}
```

### 错误响应字段

| 参数                      | 类型      | 说明      |
| ----------------------- | ------- | ------- |
| refundResult            | Object  | JSON 对象 |
| refundResult.resultCode | Numeric | 错误代码    |
| refundResult.resultDesc | String  | 错误描述    |

### 错误代码

<table><thead><tr><th width="140.05902099609375">代码</th><th>说明</th></tr></thead><tbody><tr><td>103</td><td>API 密钥无效、未找到商户或邮箱无效</td></tr><tr><td>206</td><td>未找到交易，或交易无效</td></tr><tr><td>551</td><td>重复的退款请求</td></tr><tr><td>102</td><td>内部服务器错误</td></tr></tbody></table>

## 3. 查询退款状态

### 接口 URL

```json
POST {domain}/airtime/api/restful/v2.0/Payment/retrieveRefund.json
```

### 示例

```javascript
{
  "retrieveRefundRequest": {
    "apiKey": "<your_api_key>",
    "country": "ID",
    "projectId": 100,
    "refundId": "rfl_sg1955a7330200033"
  }
}
```

### 请求参数

<table><thead><tr><th width="240.04254150390625">参数</th><th width="99.5789794921875">类型</th><th width="83.94873046875">是否必填</th><th>说明</th></tr></thead><tbody><tr><td>refundRequest</td><td>Object</td><td>是</td><td>有效的 JSON 对象</td></tr><tr><td>refundRequest.apiKey</td><td>String</td><td>是</td><td>Coda 提供的 API 密钥</td></tr><tr><td>refundRequest.country</td><td>String</td><td>否</td><td>ISO 3166 国家代码</td></tr><tr><td>refundRequest.projectId</td><td>Numeric</td><td>是</td><td>唯一的项目 ID</td></tr><tr><td>refundRequest.refundId</td><td>String</td><td>是</td><td>查询的退款请求 ID</td></tr></tbody></table>

### 响应

与 `createRefund` 接口中的 `refundResult` 对象结构相同。

## 4. 退款通知回调

Codapay 会向已配置的通知 URL 发送 webhook，告知退款状态。

### Codapay 请求

{% code overflow="wrap" %}

```javascript
GET {merchantCallbackUrl}?EventType=refund&Id=<refund_id>&CreatedAt=<ts>&StatusCode=accepted&FinalizedAt=<ts>&Type=full&TxnId=<txn_id>&AmountValue=<amount>&AmountCurrency=<currency>&OrderId=<orderId>&ResultCode=0&Checksum=<checksum>
```

{% endcode %}

### Webhook 响应规范

```javascript
ResultCode=0
```

### 关键字段

<table><thead><tr><th width="157.54168701171875">字段</th><th width="124.21441650390625">类型</th><th>说明</th></tr></thead><tbody><tr><td>EventType</td><td>String</td><td>事件类型，本接口为 "refund"</td></tr><tr><td>Id</td><td>String</td><td>退款 ID (Refund ID)</td></tr><tr><td>CreatedAt</td><td>String</td><td>退款创建时间戳（ISO 8601 格式）<br><br>支持格式：<br>YYYY-MM-DDThh:mm:ss.ssssssZ（微秒级）<br>YYYY-MM-DDThh:mm:ss.sssssssssZ（纳秒级）</td></tr><tr><td>StatusCode</td><td>String</td><td>状态：accepted、succeeded 或 failed</td></tr><tr><td>FinalizedAt</td><td>String</td><td>退款完成时间戳（ISO 8601 格式）<br><br>支持格式：<br>YYYY-MM-DDThh:mm:ss.ssssssZ（微秒级）<br>YYYY-MM-DDThh:mm:ss.sssssssssZ（纳秒级）</td></tr><tr><td>Type</td><td>String</td><td>退款类型，始终为 "full"</td></tr><tr><td>TxnId</td><td>String</td><td>交易 ID</td></tr><tr><td>AmountValue</td><td>Float</td><td>退款金额</td></tr><tr><td>AmountCurrency</td><td>String</td><td>退款币种</td></tr><tr><td>OrderId</td><td>String</td><td>商户订单号</td></tr><tr><td>ResultCode</td><td>Integer</td><td><p>支付结果代码（0 表示成功）<br><br>0: 成功<br>431: 处理中</p><p>216: 处理中</p><p>481: 处理中<br>其他: 失败</p></td></tr><tr><td>Checksum</td><td>String</td><td>校验码，用于数据验证</td></tr></tbody></table>

### 校验和生成

```javascript
MD5(eventType + apiKey + txnId + orderId + resultCode + statusCode)
```

## 5. 沙盒环境测试

### 退款

1. 在沙盒环境[完成一笔支付](https://coda-payments.gitbook.io/zhong-wen-coda-private-technical-documentation/codapay/ji-cheng-zhi-nan/sha-he-huan-jing-ce-shi-zhi-nan/sha-he-mo-ni-qi-ce-shi-zhi-nan)
2. 使用该交易 ID 发起退款请求
3. 通过 retrieveRefund 接口查询退款状态
4. 如需模拟最终状态，请联系 Codapay 支持团队

### 请求示例

#### 创建退款

```javascript
curl --location 'https://sandbox.codapayments.com/airtime/api/restful/v2.0/Payment/createRefund.json' \
--header 'Content-Type: application/json' \
--data-raw '{
  "refundRequest": {
    "country": "ID",
    "apiKey": "<test_key>",
    "txnId": "740976157910166864",
    "projectId": 43
  }
}'
```

#### 查询退款

```javascript
curl --location 'https://sandbox.codapayments.com/airtime/api/restful/v2.0/Payment/retrieveRefund.json' \
--header 'Content-Type: application/json' \
--data '{
  "retrieveRefundRequest": {
    "country": "ID",
    "apiKey": "<test_key>",
    "projectId": 43,
    "refundId": "rfl_sg1955a7330200033"
  }
}'
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.coda.co/https-coda-payments.gitbook.io-zhong-wen-coda-private-technical-documentation/codapay/tui-kuan-api.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
