> 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/zhi-fu-ye-mian-api-ji-cheng/qi-zha-he-ju-fu-tong-zhi.md).

# 欺诈和拒付通知

本参考资料描述了 Coda 发送的 webhook 通知，以便让您的后端了解拒付生命周期的变化以及交易中的早期欺诈警告。

> **注意：**&#x8981;启用拒付和欺诈更新功能，您的账户需要启用 POST 通知。请联系您的 Codapay 客户经理启用此功能。

### 概述 <a href="#overview" id="overview"></a>

Codapay 使用 webhook 来通知您的“完整通知 URL”，每当拒付事件在其生命周期中进展到一定阶段，或者您的某笔交易收到早期欺诈警告时，都会收到通知。

> 请确保已在发布商门户中配置“完整通知 URL”。有关详细说明，请参阅[“设置付款”指南。](https://claude.ai/chat/cb09ca98-8996-43b7-87cb-cd20a04c1dea#)

### 事件类型 <a href="#event-types" id="event-types"></a>

#### **拒付事件**

对于拒付生命周期事件，您的端点应准备好处理以下`eventType`值：

| `chargeback_created` | 已对某笔交易提出拒付申请        |
| -------------------- | ------------------- |
| `dispute_sent`       | 拒付事件已升级，争议已提交至支付网关。 |
| `dispute_lost`       | 争议最终以客户胜诉告终。        |
| `dispute_won`        | 纠纷最终以商家的胜诉告终。       |

#### **欺诈事件**

对于欺诈事件，您的端点应准备好处理以下`eventType`值：

| `fraud_notification` | 已收到一笔交易的早期欺诈预警。 |
| -------------------- | --------------- |

### 通知请求体结构 <a href="#notification-payload-structure" id="notification-payload-structure"></a>

通知以请求的形式发送，`POST`请求体为 JSON 格式。

| eventType           | string         | 所报告的具体事件类型（例如，`chargeback_created`）                                                                                                                                                  |
| ------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| timestamp           | string         | 通知时间戳                                                                                                                                                                                |
| data.chargebackId   | string         | Coda 的内部拒付标识符                                                                                                                                                                        |
| data.stage          | string         | 拒付的生命周期阶段。请参见下方的[阶段值。](https://coda-payments.gitbook.io/coda-public-technical-documentation/codapay/hosted-payment-page-integration/fraud-and-chargeback-notifications#stage-values) |
| data.txnId          | string         | Coda 的内部交易 ID                                                                                                                                                                        |
| data.reason         | string         | 发卡行提供的拒付原因（例如：其他欺诈 - 卡片不在场环境）                                                                                                                                                        |
| data.outcome        | string \| null | 争议的最终结果：胜诉、败诉或因尚未解决而无效。                                                                                                                                                              |
| data.amountValue    | decimal        | 交易金额                                                                                                                                                                                 |
| data.amountCurrency | string         | ISO 4217 货币代码（例如 CHF、USD）                                                                                                                                                            |
| data.orderId        | string         | <p>您的订单号（如有提供）</p><h4 id="stage-values"><br></h4>                                                                                                                                    |

### 阶段值 <a href="#stage-values" id="stage-values"></a>

| `CHARGEBACK`          | 已创建拒付机制                                                                  |
| --------------------- | ------------------------------------------------------------------------ |
| `DISPUTE_SENT`        | 争议已提交至网关                                                                 |
| `DISPUTE_LOST`        | 争议已解决——结果：败诉                                                             |
| `DISPUTE_WON`         | 争议已解决——结果：胜诉                                                             |
| `EARLY_FRAUD_WARNING` | <p>在发起拒付之前已收到欺诈信号</p><h4 id="chargeback-notification-examples"><br></h4> |

### 拒付通知示例 <a href="#chargeback-notification-examples" id="chargeback-notification-examples"></a>

`chargeback_created`

```
{
  "eventType": "chargeback_created",
  "timestamp": "1773860416000",
  "data": {
    "chargebackId": "CBKJTNRS9XDNBVQ9",
    "stage": "CHARGEBACK",
    "txnId": "7677293394619242496",
    "reason": "Other Fraud-Card Absent Environment",
    "outcome": null,
    "amountValue": 100,
    "amountCurrency": "CHF",
    "orderId": "c8856951-b495-4e9b-8b48-01123cf4f5eb"
  }
}
```

`dispute_sent`

```
{
  "eventType": "dispute_sent",
  "timestamp": "1773860416000",
  "data": {
    "chargebackId": "CBKJTNRS9XDNBVQ9",
    "stage": "DISPUTE_SENT",
    "txnId": "7677293394619242496",
    "reason": "Other Fraud-Card Absent Environment",
    "outcome": null,
    "amountValue": 100,
    "amountCurrency": "CHF",
    "orderId": "c8856951-b495-4e9b-8b48-01123cf4f5eb"
  }
}
```

`dispute_lost`

```
{
  "eventType": "dispute_lost",
  "timestamp": "1773860416000",
  "data": {
    "chargebackId": "CBKJTNRS9XDNBVQ9",
    "stage": "DISPUTE_LOST",
    "txnId": "7677293394619242496",
    "reason": "Other Fraud-Card Absent Environment",
    "outcome": "LOST",
    "amountValue": 100,
    "amountCurrency": "CHF",
    "orderId": "c8856951-b495-4e9b-8b48-01123cf4f5eb"
  }
}
```

`dispute_won`

```
{
  "eventType": "dispute_won",
  "timestamp": "1773860416000",
  "data": {
    "chargebackId": "CBKJTNRS9XDNBVQ9",
    "stage": "DISPUTE_WON",
    "txnId": "7677293394619242496",
    "reason": "Other Fraud-Card Absent Environment",
    "outcome": "WON",
    "amountValue": 100,
    "amountCurrency": "CHF",
    "orderId": "c8856951-b495-4e9b-8b48-01123cf4f5eb"
  }
}
```

### 欺诈通知示例 <a href="#fraud-notification-example" id="fraud-notification-example"></a>

`fraud_notification`

```
{
  "eventType": "fraud_notification",
  "timestamp": "1773860416000",
  "data": {
    "chargebackId": "CBKJTNRS9XDNBVQ9",
    "stage": "EARLY_FRAUD_WARNING",
    "txnId": "7677293394619242496",
    "reason": "Other Fraud-Card Absent Environment",
    "amountValue": 100,
    "amountCurrency": "CHF",
    "orderId": "c8856951-b495-4e9b-8b48-01123cf4f5eb"
  }
}
```

### 通知签名验证 <a href="#notification-signature-verification" id="notification-signature-verification"></a>

每个 POST 通知都包含两个标头，使您可以验证请求是否来自 Codapay 且未被篡改：

| `X-Request-Time` | 请求的时间戳，以时间戳毫秒表示（数字字符串）                 |
| ---------------- | -------------------------------------- |
| `X-Signature`    | Codapay 使用您的商户密钥计算的 HMAC-SHA512 十六进制摘要 |

#### **工作原理**

Codapay 和您的服务器都同意使用同一个 UTF-8 字符串进行签名：

```
X-Request-Time + "." + raw POST body (UTF-8)
```

**使用您的商户密钥（API key）**&#x5BF9;该字符串应用 HMAC-SHA512 算法，生成**128 个字符的小写十六进制**摘要。

Codapay 会将此摘要放在`X-Signature`邮件头中发送。您的服务器必须重新计算相同的十六进制摘要，并将其与接收到的值进行比较。

#### **验证步骤**

1. 读取传入请求中的请求头`X-Request-Time`和响应头。`X-Signature`
2. 读取请求的**原始字节。**
3. 构建签名字符串：（`requestTime + "." + body`解释为 UTF-8）。
4. 计算预期签名：`HMAC-SHA512(secretKey, signedString)`→ 小写十六进制。
5. 使用**恒定时间**`expectedHex`比较与头部值进行比较，以防止计时攻击。比较不区分大小写。`X-Signature`

如果值不匹配，则拒绝请求。

#### **示例 — Java**

```
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

public class SignatureValidator {

    private static final String HMAC_SHA512 = "HmacSHA512";

    public static boolean validate(String secretKey, String requestTime, byte[] body, String xSignature) {
        String bodyUtf8 = new String(body, StandardCharsets.UTF_8);
        String text = requestTime + "." + bodyUtf8;
        String expected = hmacSha512Hex(secretKey, text);
        return MessageDigest.isEqual(
            expected.toLowerCase().getBytes(),
            xSignature.toLowerCase().getBytes()
        );
    }

    private static String hmacSha512Hex(String secretKey, String text) {
        try {
            Mac mac = Mac.getInstance(HMAC_SHA512);
            mac.init(new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), HMAC_SHA512));
            byte[] raw = mac.doFinal(text.getBytes(StandardCharsets.UTF_8));
            return HEX.formatHex(raw);
        } catch (Exception e) {
            throw new IllegalStateException("HMAC-SHA512 not available", e);
        }
    }
}
```

#### 回复通知 <a href="#responding-to-notifications" id="responding-to-notifications"></a>

为确认收到通知，您的服务器必须响应一个包含`200 OK`以下 JSON 正文的响应：

```
{
  "ResultCode": 0
}
```

重试策略：如果未收到有效响应，Codapay 将每隔 5 分钟重新发送一次通知，共发送 3 次。如果所有尝试均失败，系统将向您的技术联系人发送电子邮件提醒。


---

# 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/zhi-fu-ye-mian-api-ji-cheng/qi-zha-he-ju-fu-tong-zhi.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.
