---
updatedAt: 2026-10-21T06:36:09.000Z
agentTools:
  projectIndex: https://cn-doc.trustdecision.com/llms.txt
---

# Ethoca

# Ethoca API 对接指引（修订版）

更新日期：2026-07-15<br />适用对象：客户技术团队、支付/风控/售后系统负责人<br />PSP 辅助说明：Stripe、Checkout.com、CyberSource、Worldpay、Adyen<br />预警来源：TrustDecision Ethoca Dispute Notification API

> **文档边界**：第 1～3 节定义 TrustDecision Ethoca 对接契约；第 4～7 节为交易匹配和 PSP 实施建议，不构成 TrustDecision 或 PSP 的完整 API 规范。实施前应以客户实际开通的环境、API 版本、账号权限和服务端联调结果为准。

## 1. 文档目标

本文档说明客户如何对接 TrustDecision Ethoca API，包括接收 Ethoca 拒付预警、理解预警字段、调用更新接口反馈处置结果。文档中补充的 PSP API 字段和退款接口说明，仅用于辅助客户在本地系统中完成交易匹配、退款确认和结果反馈，不作为完整 PSP 接入规范。

客户通常需要完成以下对接：

1. 接收 Ethoca 预警：客户提供 HTTPS Webhook，接收 TrustDecision 推送的 `Confirmed Fraud` 或 `Customer Dispute` 预警。
2. 反馈处置结果：退款、停止结算、未找到交易或转人工处理后，调用 Ethoca 更新接口反馈 `outcome`、`refunded`、`amountStopped`、`actionTimestamp` 等结果。
3. 可选同步 PSP 交易：如客户希望自动匹配交易或自动触发退款，可参考本文档中的 PSP 辅助字段，从 Stripe、Checkout.com、CyberSource、Worldpay 或 Adyen 拉取交易/报表/事件，沉淀到统一交易库。

核心要求：

* 预警最终处置建议在 24 小时内完成并反馈。第一个最终状态会被发送给发卡方，因此在资金动作尚未确认时不要提前提交最终状态。`in_progress`、`shipper_contacted` 仅适用于 `Confirmed Fraud`，且不应停留超过 24 小时。
* “匹配成功”不等于“允许自动退款”。自动退款必须同时通过客户授权、唯一交易匹配、金额、币种、可退余额、交易状态、幂等和风险阈值校验；否则进入人工复核。
* PSP 退款成功不等于 Ethoca 自动知晓。退款后必须调用 TrustDecision 的 Ethoca 更新接口反馈结果。
* 仅在 PSP 层真实退款或真实停止结算/履约后，才能反馈 `refunded` 或 `not settled`，不能只更新 Ethoca 状态而不执行资金动作。

## 2. 端到端集成流程

推荐客户按以下流程建设：

1. 客户在 TrustDecision 门户配置 Ethoca 预警接收 URL。
2. TrustDecision 将 Ethoca 预警以 `POST` JSON 推送给客户。
3. 客户先校验签名、时间戳和请求格式，再按 `ethocaId` 幂等落库。
4. 接收成功后返回 HTTP `200` 和 `{ "code": 200, "message": "" }`。该响应只表示已接收，不代表已完成退款；其他响应可能触发 TrustDecision 按配置重试。
5. 系统按 PSP 账号、商户号、交易日期、金额、币种、卡尾号、ARN、授权码等字段查找原交易。`cardBin` 只能与 `cardLast4` 同时使用，不得单独参与匹配。
6. 唯一候选命中后，系统继续执行客户审批和退款前置校验；仅在满足客户自动退款策略时才调用 PSP Refund API。
7. 系统监听 PSP webhook 或报表确认退款状态。
8. 系统调用 TrustDecision Ethoca 更新接口反馈最终结果。
9. 所有预警、匹配候选、退款请求、PSP 响应、Ethoca 更新响应均落库，便于争议追溯。

## 3. Ethoca 预警接入

### 3.1 预警推送接口

客户需要提供一个 HTTPS RESTful 接口，用于接收 TrustDecision 推送的 Ethoca 预警。一次推送可以包含一个或多个预警。

#### 3.1.1 在 TrustDecision 门户配置订阅服务

客户开发接收接口后，需要先在 TrustDecision 门户完成订阅配置，TrustDecision 才会把 Ethoca 预警推送到客户指定地址。

操作路径和配置项如下：

1. 进入 TrustDecision 门户，打开开发者相关页面，选择“订阅设置”。
2. 点击“添加订阅服务”，在订阅服务中选择已分配给客户的 Ethoca 预警服务。
3. 填写“推送地址”，该地址必须是客户可公网访问的 HTTPS URL，也就是本文档中的预警接收接口。
4. 按需填写“告警邮箱”。当推送失败或配置异常时，客户可通过该邮箱接收通知。
5. 如客户接口要求固定 Header，可在“请求头”处添加。Header 鉴权属于客户自定义校验，不替代 TrustDecision 推送签名。
6. 保存后，列表中会展示推送地址、`Access Key`、`Access Secret` 和操作入口。客户服务端需要使用 `Access Secret` 校验 TrustDecision 推送请求签名。

Ethoca 预警订阅服务配置示意

![](https://files.readme.io/332a85ec76b6a814af34a7bda291faa0d60c0ce236abdea7f8ec3faa3d3c6db8-image.png)

<br />

![](https://files.readme.io/5de059ae787fa849092e6a943ed5e53be1e23401ea68371d69881725d8629750-image.png)

<br />

推送请求概要：

| 项目            | 要求                                                               |
| ------------- | ---------------------------------------------------------------- |
| Method        | `POST`                                                           |
| Content-Type  | `application/json`                                               |
| Character Set | `UTF-8`                                                          |
| 鉴权参数          | `timestamp`、`partner_code`、`access_key`、`sign`                   |
| 签名            | `HmacSHA1(partner_code + timestamp + access_key, access_secret)` |
| 成功响应          | HTTP `200`，响应体 `{ "code": 200, "message": "" }`                  |

鉴权参数应放在 URL query 中，`timestamp` 使用毫秒时间戳。

推送外层结构：

请求体根节点为 JSON 数组，数组内每个元素代表一种预警类型及其预警明细。

| 字段           | 类型     | 是否必传 | 说明                                              |
| ------------ | ------ | ---- | ----------------------------------------------- |
| `type`       | String | 是    | 预警类型，取值为 `Confirmed Fraud` 或 `Customer Dispute` |
| `parameters` | Array  | 是    | 预警详情列表；同一次推送中可包含一个或多个预警对象                       |

`Confirmed Fraud` 和 `Customer Dispute` 的 `parameters` 字段并不完全一致。客户解析时应先根据外层 `type` 判断预警类型，再按对应字段表处理。除外层 `type`、`parameters` 以及已由联调确认的字段外，客户端应允许明细字段缺失或为空，不应因单个非关键字段缺失而拒绝整批预警。

#### 3.1.2 Confirmed Fraud 推送样例

`Confirmed Fraud` 是已确认欺诈预警，主要用于提示客户尽快停止履约、取消订单、退款或标记无法处理。该类型通常不包含拒付原因、拒付金额等 chargeback 字段。

```json
[
  {
    "type": "Confirmed Fraud",
    "parameters": [
      {
        "ethocaId": "1E7543HNEG001VOURNV96Q857",
        "alertTimestamp": 1720592944328,
        "age": 13,
        "issuer": "TestIssuer",
        "cardNumber": "888888******2108",
        "cardBin": "888888",
        "cardLast4": "2108",
        "arn": "435HJ7674346895HJ767434",
        "authCode": "24545564",
        "transactionTimestamp": "2024-06-10T16:13:04.328Z",
        "merchantDescriptor": "TONGDUNTEST",
        "partnerMerchantId": "TRUST_DEC_1",
        "ethocaMerchantId": 9088,
        "mcc": "3000",
        "amount": 37.55,
        "currency": "USD",
        "amountUsd": 37.55,
        "transactionType": "swiped",
        "initiatedBy": "cardholder",
        "liability": "no",
        "source": "IssuingOrganisation",
        "merchantName": "Tongdun Sub Merchant"
      }
    ]
  }
]
```

#### 3.1.3 Customer Dispute 推送样例

`Customer Dispute` 是客户争议预警。除交易基础字段外，该类型会返回拒付/争议相关字段，例如拒付原因、拒付金额、拒付币种，客户可用于判断是全额退款、部分退款还是转人工争议。

```json
[
  {
    "type": "Customer Dispute",
    "parameters": [
      {
        "ethocaId": "1D7543HNEG001VOURNV96Q857",
        "alertTimestamp": "2024-06-10T16:13:04.328Z",
        "age": 19,
        "issuer": "MerchantBank",
        "transactionTimestamp": "2023-03-10T16:13:04.328Z",
        "amount": 121.10,
        "currency": "CAD",
        "cardBin": "555456",
        "cardLast4": "1240",
        "cardNumber": "555456******1240",
        "arn": "435HJ7674346895HJ767434",
        "authCode": "21HJ767",
        "merchantDescriptor": "TONGDUNTEST",
        "partnerMerchantId": "demo_prod001",
        "ethocaMerchantId": 3001,
        "mcc": "3000",
        "amountUsd": 88.51,
        "transactionType": "swiped",
        "initiatedBy": "cardholder",
        "liability": "no",
        "source": "IssuingOrganisation",
        "merchantName": "Tongdun Sub Merchant",
        "transactionId": "875HJ767434121",
        "chargebackReasonCode": "10.1",
        "chargebackReason": "EMV Liability Shift Counterfeit Fraud",
        "chargebackAmount": 121.10,
        "chargebackCurrency": "CAD"
      }
    ]
  }
]
```

### 3.2 预警类型

| Ethoca `type`      | 说明                       | 推荐动作                       |
| ------------------ | ------------------------ | -------------------------- |
| `Confirmed Fraud`  | 发卡行或持卡人确认的欺诈预警           | 尽快停止履约/服务，匹配原交易并退款或停止结算    |
| `Customer Dispute` | 客户争议预警，通常带拒付原因、拒付金额、拒付币种 | 匹配原交易，按客户策略退款、已退款确认或进入争议处理 |

### 3.3 关键预警字段

两类预警分别建模。不要先读取“公共字段”再拼接“差异字段”；客户应根据外层 `type`，直接使用对应的完整字段表解析。不同发卡行可能不提供部分明细字段，因此除已联调确认的必传字段外，客户端应允许字段缺失或为空，并保存完整原始报文。

#### 3.3.1 Confirmed Fraud 完整字段

外层 `type` 固定为 `Confirmed Fraud`。该类型以匹配原交易、停止履约和阻止损失为主，通常不返回拒付原因、拒付金额等 Customer Dispute 字段。

| 字段                     | 类型/约束                 | 说明                                            | 处理要求                                     |
| ---------------------- | --------------------- | --------------------------------------------- | ---------------------------------------- |
| `ethocaId`             | String，最大 25 字符       | Ethoca 预警唯一 ID                                | 必须保存并作为幂等键                               |
| `alertTimestamp`       | ISO 8601 字符串或毫秒时间戳    | 预警创建时间                                        | 原始字段表与样例类型不一致；客户端应兼容两种格式，最终类型须联调确认       |
| `age`                  | Numeric               | 交易时间与预警时间的小时差                                 | 允许为空                                     |
| `issuer`               | String，最大 100 字符      | 发卡行名称                                         | 允许为空                                     |
| `cardNumber`           | String                | 完整或掩码卡号，取决于发卡行                                | 按敏感数据要求存储；不要假设总能取得完整卡号                   |
| `cardBin`              | String                | 卡号前 6 位                                       | 允许为空；不得单独参与匹配，使用时必须同时命中 `cardLast4`      |
| `cardLast4`            | String，4 位            | 卡号后 4 位                                       | 允许为空；可与授权码、交易时间和交易金额组合匹配，不得单独确认交易        |
| `arn`                  | String，最大 64 字符       | Acquirer Reference Number                     | 已结算交易的强匹配字段；允许为空                         |
| `authCode`             | String，最大 8 字符        | 授权码                                           | 注意字段名大小写；允许为空                            |
| `transactionTimestamp` | ISO 8601 UTC          | 原交易授权时间                                       | 与 PSP 交易时间的允许偏差为 ±72 小时                  |
| `merchantDescriptor`   | String，最大 50 字符       | 账单描述符                                         | 商户路由辅助                                   |
| `partnerMerchantId`    | String，最大 50 字符       | 合作方平台商户 ID                                    | 商户路由辅助                                   |
| `ethocaMerchantId`     | Number/String，以实际报文为准 | Ethoca 商户 ID                                  | 建议原样保存，类型须联调确认                           |
| `merchantName`         | String，最大 60 字符       | Ethoca 商户/子商户名称                               | 允许为空                                     |
| `mcc`                  | String，最大 4 字符        | 商户类别码                                         | 允许为空                                     |
| `amount`               | Decimal               | 原交易金额                                         | 必须与 `currency` 一起使用；同币种或跨币种换算后允许偏差均为 ±5% |
| `currency`             | String，ISO 4217 三位代码  | 原交易币种                                         | 不得默认使用 USD；币种不同时按确认的汇率源换算后再比较            |
| `amountUsd`            | Decimal               | 折算美元金额                                        | 仅用于分析，不替代原币金额                            |
| `transactionType`      | String 枚举             | `keyed` / `swiped` / `e-commerce` / `unknown` | 未知值应兼容存储                                 |
| `initiatedBy`          | String 枚举             | `issuer` / `cardholder` / `not_available`     | 未知值应兼容存储                                 |
| `liability`            | String 枚举             | `yes` / `no` / `not_available`                | 仅作责任转移辅助判断                               |
| `source`               | String，最大 50 字符       | 预警来源                                          | 可能不返回                                    |

如果没有匹配到交易，建议按 `arn`、`authCode`、`cardLast4`、金额、币种和交易时间窗口逐级降级匹配。若使用 `cardBin`，必须同时命中 `cardLast4`；无法确认唯一交易时必须转人工复核。

#### 3.3.2 Customer Dispute 完整字段

外层 `type` 固定为 `Customer Dispute`。除交易识别字段外，该类型还可能包含 `transactionId`、拒付原因、拒付金额和拒付币种。

| 字段                     | 类型/约束                 | 说明                                            | 处理要求                                                                                           |
| ---------------------- | --------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `ethocaId`             | String，最大 25 字符       | Ethoca 预警唯一 ID                                | 必须保存并作为幂等键                                                                                     |
| `alertTimestamp`       | ISO 8601 字符串或毫秒时间戳    | 预警创建时间                                        | 客户端应兼容两种格式，最终类型须联调确认                                                                           |
| `age`                  | Numeric               | 交易时间与预警时间的小时差                                 | 允许为空                                                                                           |
| `issuer`               | String，最大 100 字符      | 发卡行名称                                         | 允许为空                                                                                           |
| `cardNumber`           | String                | 完整或掩码卡号，取决于发卡行                                | 按敏感数据要求存储；不要假设总能取得完整卡号                                                                         |
| `cardBin`              | String                | 卡号前 6 位                                       | 允许为空；不得单独参与匹配，使用时必须同时命中 `cardLast4`                                                            |
| `cardLast4`            | String，4 位            | 卡号后 4 位                                       | 允许为空；可与授权码、交易时间和交易金额组合匹配，不得单独确认交易                                                              |
| `arn`                  | String，最大 64 字符       | Acquirer Reference Number                     | 已结算交易的强匹配字段；允许为空                                                                               |
| `authCode`             | String，最大 8 字符        | 授权码                                           | 注意字段名大小写；允许为空                                                                                  |
| `transactionTimestamp` | ISO 8601 UTC          | 原交易授权时间                                       | 与 PSP 交易时间的允许偏差为 ±72 小时                                                                        |
| `merchantDescriptor`   | String，最大 50 字符       | 账单描述符                                         | 商户路由辅助                                                                                         |
| `partnerMerchantId`    | String，最大 50 字符       | 合作方平台商户 ID                                    | 商户路由辅助                                                                                         |
| `ethocaMerchantId`     | Number/String，以实际报文为准 | Ethoca 商户 ID                                  | 建议原样保存，类型须联调确认                                                                                 |
| `merchantName`         | String，最大 60 字符       | Ethoca 商户/子商户名称                               | 允许为空                                                                                           |
| `mcc`                  | String，最大 4 字符        | 商户类别码                                         | 允许为空                                                                                           |
| `amount`               | Decimal               | 原交易金额                                         | 必须与 `currency` 一起使用；同币种或跨币种换算后允许偏差均为 ±5%                                                       |
| `currency`             | String，ISO 4217 三位代码  | 原交易币种                                         | 不得默认使用 USD；币种不同时按确认的汇率源换算后再比较                                                                  |
| `amountUsd`            | Decimal               | 折算美元金额                                        | 仅用于分析，不替代原币金额                                                                                  |
| `transactionType`      | String 枚举             | `keyed` / `swiped` / `e-commerce` / `unknown` | 未知值应兼容存储                                                                                       |
| `initiatedBy`          | String 枚举             | `issuer` / `cardholder` / `not_available`     | 未知值应兼容存储                                                                                       |
| `liability`            | String 枚举             | `yes` / `no` / `not_available`                | 仅作责任转移辅助判断                                                                                     |
| `source`               | String，最大 50 字符       | 预警来源                                          | 可能不返回                                                                                          |
| `transactionId`        | String，最大 65 字符       | Ethoca 返回的卡组织交易标识                             | 仅保存用于预警追溯；不得与 PSP transaction ID、network transaction ID、payment ID 或客户 order ID/reference 直接匹配 |
| `chargebackReasonCode` | String                | 拒付原因代码                                        | 风控辅助                                                                                           |
| `chargebackReason`     | String                | 拒付原因说明                                        | 风控辅助                                                                                           |
| `chargebackAmount`     | Decimal               | 拒付金额                                          | 不一定等于原交易金额                                                                                     |
| `chargebackCurrency`   | String，ISO 4217 三位代码  | 拒付币种                                          | 必须和 `chargebackAmount` 一起处理                                                                    |

`chargebackAmount` 不一定等于原交易 `amount`。自动退款前应同时校验 PSP 可退余额、客户退款策略、原交易金额和预警金额，避免超额退款或重复退款。

### 3.4 Ethoca 更新接口

退款、停止结算或人工处理完成后，客户需要调用 TrustDecision Ethoca 更新接口反馈结果。单次请求仅允许一个更新事件。

建议客户先看本节的“请求体字段表”，再看后面的枚举说明和样例。重复预警场景下，历史预警 ID 放在请求对象同一层的 `historicalEthocaId` 字段中。

| 环境         | Base URL                                         |
| ---------- | ------------------------------------------------ |
| Sandbox    | `https://sg-s.apitd.net/api/ethoca/alert/update` |
| Production | `https://sg.apitd.net/api/ethoca/alert/update`   |

请求概要：

| 项目           | 要求                                        |
| ------------ | ----------------------------------------- |
| Method       | `POST`                                    |
| Content-Type | `application/json`                        |
| 鉴权参数         | URL query 中传 `partner_code`、`partner_key` |
| 请求体          | JSON 数组，但当前每次只放一个对象                       |

#### 3.4.1 请求参数

URL query 参数：

| 字段             | 类型     | 是否必传 | 说明                             |
| -------------- | ------ | ---- | ------------------------------ |
| `partner_code` | String | 是    | TrustDecision 分配的 Partner Code |
| `partner_key`  | String | 是    | TrustDecision 分配的 Partner Key  |

请求体字段：

请求体是 JSON 数组；下表字段均放在数组内单个更新对象中。重复预警时，`historicalEthocaId` 与 `ethocaId`、`matchTransactionResult` 平级。

| 字段                       | 类型      | 是否必传 | 说明                                                                      | 取值/格式                                             |
| ------------------------ | ------- | ---- | ----------------------------------------------------------------------- | ------------------------------------------------- |
| `ethocaId`               | String  | 是    | 当前需要更新的 Ethoca 预警 ID                                                    | 最大 25 位                                           |
| `outcome`                | String  | 是    | 预警处置结果                                                                  | 严格使用 3.5 中列出的枚举                                   |
| `refunded`               | String  | 是    | 交易是否已退款、未退款或未结算                                                         | `refunded` / `not refunded` / `not settled`       |
| `firstPartyFraud`        | String  | 否    | 一方欺诈判断：持卡人声称未授权，但商户有充分依据认为持卡人知情、默许或从交易中获益                               | `yes` / `no`                                      |
| `amountStopped`          | Decimal | 否    | 实际阻止或部分退款金额；主要用于 `partially_stopped`，也可在 `other` / `resolved` 的部分退款场景记录 | 大于 0，且小于或等于预警金额                                   |
| `actionTimestamp`        | String  | 建议传  | 因预警进行退款、停止结算或其他动作的时间                                                    | ISO 8601，例如 `2024-06-10T18:33:04.328Z`            |
| `comments`               | String  | 建议传  | 备注；原始规范标记为 Optional，建议客户端固定传入以保持请求结构稳定                                  | 最大 250 字符；是否允许中文及 `other` / `Other` 时是否强制非空，须联调确认 |
| `matchTransactionResult` | String  | 建议传  | 预警匹配结果                                                                  | 严格使用 3.6 中列出的枚举                                   |
| `historicalEthocaId`     | String  | 条件必传 | 之前已处理的 Ethoca ID                                                        | 当 `matchTransactionResult=Duplicate alert` 时必传    |

#### 3.4.2 Duplicate alert 完整请求样例

以下样例表示当前预警 `2JFG5ULCLB8OQMWHQ7OAY7G7D` 是重复预警，历史上已经处理过 `1E7543HNEG001VOURNV96Q857`。注意：

* `matchTransactionResult` 必须传 `Duplicate alert`，中间使用空格。
* `historicalEthocaId` 放在请求对象同一层，和 `ethocaId`、`outcome`、`comments` 平级。
* 建议固定传 `comments`；本例不是 `other` / `Other` 场景，所以传空字符串。

```bash
curl -X POST "https://sg.apitd.net/api/ethoca/alert/update?partner_code=xxx&partner_key=xxx" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "ethocaId": "2JFG5ULCLB8OQMWHQ7OAY7G7D",
      "outcome": "stopped",
      "refunded": "refunded",
      "firstPartyFraud": "no",
      "amountStopped": 37.55,
      "actionTimestamp": "2024-06-10T18:33:04.328Z",
      "comments": "",
      "matchTransactionResult": "Duplicate alert",
      "historicalEthocaId": "1E7543HNEG001VOURNV96Q857"
    }
  ]'
```

#### 3.4.3 更新响应参数

| JSON 路径                                                  | 类型      | 说明                                  |
| -------------------------------------------------------- | ------- | ----------------------------------- |
| `code`                                                   | Integer | 顶层业务状态码；`200` 只表示请求被服务端处理，不保证更新对象成功 |
| `message`                                                | String  | 顶层状态信息；异常时返回错误原因                    |
| `alertUpdateResponses`                                   | Array   | 单条预警更新结果列表                          |
| `alertUpdateResponses[].ethocaId`                        | String  | 本次更新的 Ethoca 预警 ID                  |
| `alertUpdateResponses[].status`                          | String  | `success` / `failed`                |
| `alertUpdateResponses[].updateEventErrors`               | Array   | 单条更新失败时的错误列表                        |
| `alertUpdateResponses[].updateEventErrors[].code`        | String  | 错误码                                 |
| `alertUpdateResponses[].updateEventErrors[].description` | String  | 错误描述                                |

客户端必须同时检查 HTTP 状态、顶层 `code` 和 `alertUpdateResponses[].status`。即使顶层 `code=200`，单条更新仍可能是 `failed`。

成功响应样例：

```json
{
  "code": 200,
  "message": "Success",
  "alertUpdateResponses": [
    {
      "ethocaId": "2JFG5ULCLB8OQMWHQ7OAY7G7D",
      "status": "success"
    }
  ]
}
```

单条更新失败样例：

```json
{
  "code": 200,
  "message": "Success",
  "alertUpdateResponses": [
    {
      "ethocaId": "2JFG5ULCLB8OQMWHQ7OAY7G7D",
      "status": "failed",
      "updateEventErrors": [
        {
          "code": "6",
          "description": "OutcomeQueueItem outcome should be valid value and not null"
        }
      ]
    }
  ]
}
```

顶层异常样例：

```json
{
  "code": 9400,
  "message": "System Error"
}
```

原始文档列出了 `200`、`001`、`9400`、`9401`、`9403` 等业务码，但原表存在排版和含义不一致。正式上线前，应由 TrustDecision 提供完整错误码表；客户端不得只对 `200` 编码，其余未知业务码均应按失败处理并记录完整响应。

### 3.5 outcome 与 refunded 取值

下表是接口允许的组合，不是推荐值。表外组合可能导致更新失败。

`Confirmed Fraud` 合法组合：

| outcome                | 含义              | `refunded` | `not refunded` | `not settled` |
| ---------------------- | --------------- | :--------: | :------------: | :-----------: |
| `stopped`              | 已停止订单/服务或退款阻止损失 |      ✓     |                |       ✓       |
| `partially_stopped`    | 只阻止部分金额或部分履约    |      ✓     |        ✓       |       ✓       |
| `previously_cancelled` | 交易此前已取消         |      ✓     |                |       ✓       |
| `missed`               | 太晚，已发货或服务已使用    |      ✓     |        ✓       |       ✓       |
| `notfound`             | 未找到订单           |            |        ✓       |               |
| `account_suspended`    | 已暂停账户           |      ✓     |        ✓       |       ✓       |
| `in_progress`          | 正在处理中，仅为中间态     |      ✓     |        ✓       |       ✓       |
| `shipper_contacted`    | 已联系物流拦截，仅为中间态   |      ✓     |        ✓       |       ✓       |
| `other`                | 其他情况            |      ✓     |        ✓       |       ✓       |

`Customer Dispute` 合法组合：

| outcome               | 含义              | `refunded` | `not refunded` | `not settled` |
| --------------------- | --------------- | :--------: | :------------: | :-----------: |
| `resolved`            | 已与客户解决，预计不会发生拒付 |      ✓     |        ✓       |       ✓       |
| `previously_refunded` | 收到预警前已经退款       |      ✓     |                |       ✓       |
| `unresolved_dispute`  | 商户不同意争议原因，将继续争议 |            |        ✓       |               |
| `notfound`            | 未找到订单           |            |        ✓       |               |
| `other`               | 其他情况            |      ✓     |        ✓       |       ✓       |

> `in_progress` 和 `shipper_contacted` 仅属于 `Confirmed Fraud`。原始规范说明：如果客户对 `Customer Dispute` 发送 Confirmed Fraud outcome，Ethoca 可能执行自动映射，例如 `stopped → resolved`、`in_progress → other`。新接入不应依赖该兼容行为，应直接发送与预警类型对应的 outcome。

`refunded` 可用取值：

| refunded       | 使用条件                                             |
| -------------- | ------------------------------------------------ |
| `refunded`     | PSP 已确认退款成功或已按客户确认标准进入不可撤销处理阶段；不得仅凭退款 API 已受理就填写 |
| `not refunded` | 未退款，例如未找到交易、拒绝退款、转人工争议                           |
| `not settled`  | 原交易未结算或已取消/void/reversal，不需要走 refund             |

### 3.6 matchTransactionResult 枚举

`matchTransactionResult` 是已上线接口枚举，必须严格按下表传值，不要改大小写、空格或下划线。

| 枚举值                                                             | 场景                 | 额外要求                             |
| --------------------------------------------------------------- | ------------------ | -------------------------------- |
| `Duplicate alert`                                               | 重复预警               | 必须传 `historicalEthocaId`         |
| `Authentication failed`                                         | 支付认证失败             | 无                                |
| `Payment declined`                                              | 支付被拒绝              | 无                                |
| `Chargeback received after refunding the alert within 24 hours` | 收到预警后退款 24 小时内产生拒付 | 无                                |
| `Unmatched transaction alert`                                   | 未匹配交易的预警           | 无                                |
| `Chargeback received before alert`                              | 拒付后收到预警            | 无                                |
| `Refunded before alert`                                         | 收到预警前已退款           | 无                                |
| `Other`                                                         | 其他                 | 建议 `comments` 传非空说明；是否服务端强制须联调确认 |

<br />

## 4. 匹配逻辑

### 4.1 推荐匹配顺序

匹配流程只分为两种情况：`arn` 匹配，以及 `arn` 缺失时的组合字段匹配。

#### 4.1.1 `arn` 匹配

当 Ethoca 预警包含 `arn` 时，使用 `arn` 与 PSP 原交易进行完全一致匹配：

* `arn` 完全一致：视为完全匹配，建议退款。
* `arn` 不一致或查询到多个候选：不得继续按弱字段自动匹配，建议人工复核。

`arn` 完全匹配可以免除后续组合字段匹配，但实际退款仍须通过第 4.3 节的交易状态、可退余额、幂等和权限校验。

#### 4.1.2 `arn` 缺失时的匹配

只有当 Ethoca 预警未返回 `arn` 时，才使用 `transactionTimestamp`、`authCode`、`cardBin`、`cardLast4` 和 `amount` 组合匹配。

1. 先使用 `transactionTimestamp ±72 小时`查询 PSP 候选交易。
2. Ethoca `transactionTimestamp` 为 UTC+0。商户必须将本地系统或 PSP 返回的交易时间转换到 UTC+0，或将双方转换到同一时区后再比较。
3. 只保留满足金额容差的候选交易：
   * 同币种、不需要换算：Ethoca `amount` 与 PSP 原交易金额的偏差必须在 ±5% 内。
   * 跨币种、需要换算：按交易日汇率换算后，换算金额与 PSP 原交易金额的偏差必须在 ±5% 内。
4. 在同时满足时间和金额条件的候选交易中，按第 4.2 节的五条规则依次匹配。

匹配限制：

* `cardBin` 不能单独使用，必须和 `cardLast4` 同时匹配。
* `transactionId`、PSP transaction ID、network transaction ID、payment ID、客户 order ID 和 reference 均不参与匹配。
* 金额匹配使用 Ethoca 原交易 `amount`，不使用 `amountUsd` 或 `chargebackAmount`。
* 多个 PSP 交易同时满足同一规则时，不视为匹配成功，转人工复核。

### 4.2 匹配组合与处置建议

下表按匹配置信度从高到低排序。参考分值用于表达相对置信度，不属于 Ethoca 接口规范，客户可根据历史回放结果调整。除 `arn` 规则外，其余规则仅在 `arn` 缺失时执行。

`transactionTimestamp` 表示 UTC+0 时间窗口 ±72 小时；`amount` 表示同币种直接比较 ±5%，或跨币种换算后比较 ±5%。

| 优先级 | 匹配条件                                                             | 参考分值 | 置信评价   | 处理建议      |
| --: | ---------------------------------------------------------------- | ---: | ------ | --------- |
|   1 | `arn`                                                            |  100 | 完全匹配   | 建议退款      |
|   2 | `authCode + cardBin + cardLast4 + transactionTimestamp + amount` |   95 | 高置信匹配  | 唯一命中时建议退款 |
|   3 | `authCode + cardLast4 + transactionTimestamp + amount`           |   90 | 高置信匹配  | 唯一命中时建议退款 |
|   4 | `authCode + transactionTimestamp + amount`                       |   85 | 高置信匹配  | 建议退款      |
|   5 | `cardBin + cardLast4 + transactionTimestamp + amount`            |   80 | 中等置信匹配 | 建议人工复核    |
|   6 | `cardLast4 + transactionTimestamp + amount`                      |   70 | 低置信匹配  | 建议人工复核    |

执行要求：

* `arn` 完全一致时直接采用优先级 1，不再执行后续组合。
* `arn` 缺失时，从优先级 2 开始依次判断；某条规则唯一命中时停止后续规则。
* `cardBin` 必须与 `cardLast4` 同时使用，不能单独参与匹配。
* 没有命中、出现多个候选或关键字段冲突时，建议人工复核。

### 4.3 自动退款前置校验

仅当客户明确启用自动退款且以下条件全部满足时，系统才可以执行自动退款：

* 第 4.2 节处理建议为“建议退款”的规则唯一命中；处理建议为“建议人工复核”的规则不得直接触发自动退款。
* PSP 交易已成功 capture/settle，且未全额退款。
* 交易未被正式 dispute/chargeback 完全占用余额。
* 退款金额不超过可退余额。
* 退款币种与原交易授权币种一致。
* 客户已授权该 PSP 账号的退款权限。
* 同一 `ethoca_id + provider_payment_id + amount` 不存在成功或进行中的退款任务。
* 大额、重复预警、预警前已拒付、多个候选或关键字段冲突等场景已按客户规则转人工。跨币种交易在按交易日汇率换算且金额偏差不超过 ±5% 时，可继续执行既定匹配规则。

## 5. PSP 字段采集原则

### 5.1 客户必须额外保存的字段

不是所有 PSP 都稳定返回完整 `arn`、`authCode` 等信息。客户在支付创建和支付成功时应保存以下数据，用于候选交易筛选、退款执行、对账和审计。保存某个字段不代表该字段可以直接参与 Ethoca 匹配；匹配字段和组合必须遵循第 4 节。

* 商户订单号、PSP payment id、charge/capture/action id。
* PSP merchant account、entity、子商户 ID、账单描述符。
* 授权成功时间、capture 时间、结算时间。
* 金额、币种、退款余额。
* 卡 BIN、卡尾号、卡品牌、发卡国家；匹配时 BIN 必须与尾号同时使用。
* 授权码、ARN、RRN、network transaction ID，只要 PSP webhook、action、report 或后台可返回就保存。其中 RRN 和 network transaction ID 仅作内部查询和审计，不与 Ethoca `transactionId` 直接匹配。
* 3DS 结果、liability shift、ECI、CAVV/DS transaction id，可用于争议策略。

## 6. PSP 对接方案

### 6.1 Stripe

官方文档：

* List PaymentIntents: <https://docs.stripe.com/api/payment_intents/list>
* List Charges: <https://docs.stripe.com/api/charges/list>
* Create Refund: <https://docs.stripe.com/api/refunds/create>
* Charge object: <https://docs.stripe.com/api/charges/object>
* Charge `authorization_code`: <https://docs.stripe.com/changelog/acacia/2024-09-30/displays-authorization-code-for-charges>
* Charge `network_transaction_id`: <https://docs.stripe.com/changelog/acacia/2024-12-18/charge-display-network-transaction-id>
* Refund references: <https://docs.stripe.com/refunds>

#### 6.1.1 拉取交易

优先同步 `PaymentIntent` 获取订单级状态，再同步或 expand `Charge` 获取卡、退款、争议和网络字段。

```bash
curl -G https://api.stripe.com/v1/payment_intents \
  -u sk_live_xxx: \
  -d "created[gte]=1782748800" \
  -d "created[lte]=1782835199" \
  -d "limit=100"
```

```bash
curl -G https://api.stripe.com/v1/charges \
  -u sk_live_xxx: \
  -d "created[gte]=1782748800" \
  -d "created[lte]=1782835199" \
  -d "limit=100"
```

分页使用 `has_more` 和 `starting_after`。建议保存 5-10 分钟重叠窗口，避免边界漏单。

#### 6.1.2 用于 Ethoca 匹配的字段

| Ethoca 字段 | Stripe 字段/处理方式                                                                                                           |
| --------- | ------------------------------------------------------------------------------------------------------------------------ |
| 金额/币种     | `amount`、`amount_captured`、`currency`                                                                                    |
| 交易时间      | `created`                                                                                                                |
| 卡尾号       | `payment_method_details.card.last4`                                                                                      |
| 卡品牌/国家/类型 | `payment_method_details.card.brand`、`country`、`funding`                                                                  |
| 授权码       | `payment_method_details.card.authorization_code`，需使用支持该字段的 Stripe API 版本并按账号验证                                           |
| ARN       | Stripe 原始付款 Charge 对象没有统一的 `arn` 字段；不要承诺客户一定能通过 API 取到原始付款 ARN                                                           |
| 退款引用号     | Refund 对象的 `destination_details.card.reference`；当 `reference_type=acquirer_reference_number` 时表示退款 ARN，仅用于退款追踪，不用于匹配原始付款 |
| 账单描述符     | `calculated_statement_descriptor`                                                                                        |
| 拒付状态      | `disputed`、Dispute webhook                                                                                               |

在 Stripe 中，客户人工从后台找这些字段不直观，系统对接时应直接从 `Charge` 对象读取。推荐优先级：

1. 不得将 Ethoca `transactionId` 与 Stripe `network_transaction_id`、Charge ID、PaymentIntent ID 或订单号直接匹配。
2. Stripe 能返回 `authorization_code` 时，优先使用授权码，并结合 `last4`、金额、币种和时间窗口匹配。
3. 缺少授权码时，只能使用 `last4 + 金额 + 币种 + 时间窗口` 筛选唯一候选，并进入人工复核。
4. Stripe 的 BIN 不得单独与金额或时间组合匹配；如使用 BIN，必须同时命中 `last4`。
5. Stripe 原始付款 Charge 没有统一 ARN 字段，且退款 `destination_details.card.reference` 是退款引用号，不得用于匹配原始付款。该引用号还可能在退款发起后延迟生成。

`Charge` 对象中建议保存的字段示例：

```json
{
  "id": "ch_xxx",
  "payment_intent": "pi_xxx",
  "amount": 12110,
  "currency": "cad",
  "created": 1718029984,
  "payment_method_details": {
    "card": {
      "brand": "visa",
      "last4": "1240",
      "authorization_code": "21HJ767"
    }
  }
}
```

#### 6.1.3 发起退款

```bash
curl https://api.stripe.com/v1/refunds \
  -u sk_live_xxx: \
  -H "Idempotency-Key: refund:stripe:acct_123:pi_xxx:ethoca_123:1000" \
  -d payment_intent=pi_xxx \
  -d amount=1000 \
  -d reason=fraudulent \
  -d "metadata[ethoca_id]=1E7543HNEG001VOURNV96Q857"
```

要点：

* `amount` 是最小货币单位。
* 可传 `payment_intent` 或 `charge`，建议保存两者。
* 监听 `refund.updated`、`charge.refunded`、`charge.dispute.created`，并定时对账。
* 如果 Ethoca 为 `Confirmed Fraud` 且客户策略确认欺诈，可用 `reason=fraudulent`，但需客户确认这会影响风控拦截名单策略。

#### 6.1.4 Ethoca 反馈建议

| PSP 结果                            | Confirmed Fraud                                          | Customer Dispute                                                                                 |
| --------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| 退款已通过 webhook/对账确认成功              | `outcome=stopped`，`refunded=refunded`                    | `outcome=resolved`，`refunded=refunded`                                                           |
| 收到预警前已退款                          | 使用 Confirmed Fraud 合法组合并结合实际业务；不得传 `previously_refunded` | `outcome=previously_refunded`，`refunded=refunded`，`matchTransactionResult=Refunded before alert` |
| PaymentIntent 未 capture 且已成功取消/冲正 | `outcome=previously_cancelled`，`refunded=not settled`    | `outcome=resolved`，`refunded=not settled`                                                        |
| 找不到交易                             | `outcome=notfound`，`refunded=not refunded`               | `outcome=notfound`，`refunded=not refunded`                                                       |

### 6.2 Checkout.com

官方文档：

* Search and filter payments: <https://www.checkout.com/docs/payments/manage-payments/search-and-filter-payments>
* Payments API reference: <https://api-reference.checkout.com/tag/Payments/>
* Refund a payment: <https://www.checkout.com/docs/payments/manage-payments/refund-a-payment>

#### 6.2.1 拉取交易

使用 Payment Search API 查询交易。该接口需要 `payments:search` scope。以下使用官方已给出的 payment ID 查询语法演示请求结构；批量同步时应按 Checkout.com 当前支持的可搜索字段构造 `query`，并配合 `from`/`to` 限制时间窗口。

```bash
curl https://{prefix}.api.checkout.com/payments/search \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "query": "id:'pay_mbabizu24mvu3mela5njyhpit4'",
  "from": "2026-06-01T00:00:00Z",
  "to": "2026-06-02T00:00:00Z",
  "limit": 100
}
JSON
```

`query` 是必填字符串，最大 1024 字符；`limit` 范围为 1～1000。当前 Payment Search 请求体不支持 `sort` 字段。分页使用响应中的 `_links.next.href`，不要自行拼接下一页 URL。

#### 6.2.2 补拉 actions

Checkout.com 的强匹配字段通常在 payment actions 中更完整，建议对候选交易调用 Get payment actions。

| Ethoca 字段  | Checkout.com 字段                                          |
| ---------- | -------------------------------------------------------- |
| 金额/币种      | payment/action `amount`、`currency`                       |
| 交易时间       | `requested_on`、action `processed_on`                     |
| 授权码        | action `auth_code`                                       |
| ARN        | action `processing.acquirer_reference_number`            |
| 卡尾号        | source/card `last4`，或客户侧保存                               |
| 卡 BIN + 尾号 | source/card `bin` + `last4`；BIN 只有与尾号同时命中时才可使用           |
| 可退余额       | `balances.available_to_refund`、`balances.total_refunded` |

`reference`、`metadata`、RRN、`acquirer_transaction_id` 和 payment ID 可以用于客户内部查询、退款执行和审计，但不得与 Ethoca `transactionId` 或客户订单号组合成 Ethoca 直接匹配规则。

#### 6.2.3 发起退款

```bash
curl -X POST https://{prefix}.api.checkout.com/payments/pay_xxx/refunds \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -H "Cko-Idempotency-Key: refund:checkout:pay_xxx:ethoca_123:1000" \
  -d '{
    "amount": 1000,
    "reference": "ethoca_1E7543HNEG001VOURNV96Q857",
    "metadata": {
      "ethoca_id": "1E7543HNEG001VOURNV96Q857"
    }
  }'
```

`capture_action_id` 目前仅适用于 Checkout.com 文档明确支持的 PayPal 和 Riverty 场景，不应作为普通卡支付多 capture 的通用退款参数。

#### 6.2.4 Ethoca 反馈建议

Checkout.com 的 actions 中常能取得 `auth_code` 和 ARN；匹配仍须遵循第 4 节约束。退款成功后反馈：

* `Confirmed Fraud`：`outcome=stopped`，`refunded=refunded`。
* `Customer Dispute`：本次预警后退款用 `outcome=resolved`；仅在预警到达前已经退款时使用 `outcome=previously_refunded`。两者的 `refunded` 均按实际退款状态填写。
* 若 payment 状态为 declined：`matchTransactionResult=Payment declined`，`refunded=not refunded`。

### 6.3 CyberSource

官方文档：

* Transaction Search API: <https://developer.cybersource.com/docs/cybs/en-us/txn-search/developer/all/rest/txn-search.html>
* API Reference: <https://developer.cybersource.com/api-reference-assets/index.html>

#### 6.3.1 拉取交易

使用 Transaction Search API 按 `submitTimeUtc` 查询。查询接口支持多种 PSP 内部字段，但 Ethoca 匹配仅使用本文明确允许的金额、币种、时间、卡尾号、BIN+尾号、ARN 和授权码组合。

```bash
curl -X POST https://api.cybersource.com/tss/v2/searches \
  -H "v-c-merchant-id: your_merchant_id" \
  -H "Content-Type: application/json" \
  -H "Date: Mon, 06 Jul 2026 10:00:00 GMT" \
  -H "Digest: SHA-256=..." \
  -H "Signature: ..." \
  -d '{
    "query": "submitTimeUtc:[2026-06-01T00:00:00Z TO 2026-06-02T00:00:00Z]",
    "offset": 0,
    "limit": 100,
    "sort": "submitTimeUtc:asc"
  }'
```

分页递增 `offset` 并以账号实际允许的 `limit` 为准。不要未经验证直接使用超大页长；Transaction Search/Details 可能存在数据延迟，不应作为唯一实时交易状态来源。

批量对账可补充使用 Reporting API，例如 purchase/refund 明细报表。

#### 6.3.2 用于 Ethoca 匹配的字段

| Ethoca 字段  | CyberSource 字段                                                                 |
| ---------- | ------------------------------------------------------------------------------ |
| 金额/币种      | `orderInformation.amountDetails.totalAmount`、`currency`                        |
| 交易时间       | `submitTimeUtc`                                                                |
| 卡尾号        | `paymentInformation.card.suffix`                                               |
| 卡 BIN + 尾号 | `paymentInformation.card.prefix` + `paymentInformation.card.suffix`；BIN 不得单独使用 |
| 授权码        | `processorInformation.approvalCode`                                            |
| 商户维度       | `merchantId`、`merchantInformation.accountId`                                   |

`clientReferenceInformation.code`、RRN、`processorInformation.networkTransactionId` 和 PSP `id` 可用于客户内部查询、退款执行和审计，但不参与 Ethoca `transactionId`/order ID 匹配。

#### 6.3.3 发起退款

```bash
curl -X POST https://api.cybersource.com/pts/v2/payments/6281765770796275003009/refunds \
  -H "v-c-merchant-id: your_merchant_id" \
  -H "Content-Type: application/json" \
  -H "Date: Mon, 06 Jul 2026 10:00:00 GMT" \
  -H "Digest: SHA-256=..." \
  -H "Signature: ..." \
  -d '{
    "clientReferenceInformation": {
      "code": "ethoca_1E7543HNEG001VOURNV96Q857"
    },
    "orderInformation": {
      "amountDetails": {
        "totalAmount": "10.00",
        "currency": "USD"
      }
    }
  }'
```

注意：CyberSource 退款金额通常传 decimal 字符串，不是 minor unit。

#### 6.3.4 Ethoca 反馈建议

* ARN 完全一致时视为完全匹配。ARN 缺失时，必须按第 4.2 节组合 `approvalCode`、card prefix + suffix 或 suffix、UTC 时间窗口和金额容差；card prefix 不得单独使用。
* 若 search 找不到，但客户后台有订单，可用订单侧 PSP id 补查 Transaction Details。
* 退款成功后：`Confirmed Fraud` 使用 `stopped + refunded`，`Customer Dispute` 使用 `resolved + refunded`。找不到交易时，两类预警均使用 `notfound + not refunded`。

### 6.4 Worldpay

官方文档：

* Payment Queries: <https://docs.worldpay.com/access/products/payment-queries>
* Refund a payment: <https://docs.worldpay.com/access/products/card-payments/v5/refund-a-payment>

#### 6.4.1 拉取交易

Worldpay Access Payment Queries 支持按日期范围查询卡支付，并返回交易、支付工具、事件和可执行操作链接。

```bash
curl "https://try.access.worldpay.com/paymentQueries/payments?startDate=2026-06-01T00:00:00Z&endDate=2026-06-02T00:00:00Z&pageSize=300" \
  -u "username:password" \
  -H "Accept: application/vnd.worldpay.payment-queries-v1.hal+json"
```

可用过滤包括 `entityReferences`、`currency`、`minAmount`、`maxAmount`、`last4Digits`、`receivedEvents`。该服务是数据聚合服务，通常会有短暂延迟，不应作为唯一实时来源。

#### 6.4.2 用于 Ethoca 匹配的字段

| Ethoca 字段 | Worldpay 字段                                              |
| --------- | -------------------------------------------------------- |
| 金额/币种     | payment value amount/currency                            |
| 交易时间      | payment/event timestamp                                  |
| 卡尾号       | 查询参数 `last4Digits` 和返回的 payment instrument               |
| 授权码       | `issuer.authorizationCode`；仅在返回该字段时使用                    |
| 商户维度      | `entityReferences`                                       |
| 可退款动作     | `_links.payments:refund`、`_links.payments:partialRefund` |

`scheme.reference`、`transactionReference` 和 Worldpay payment ID 可用于客户内部查询、退款执行和审计，但不得与 Ethoca `transactionId` 或客户 order ID/reference 直接匹配。

#### 6.4.3 发起退款

Worldpay 不建议硬编码退款 URL。应使用 payment/sale/settlement 响应中的 action link。

全额退款：

```bash
curl -X POST "https://try.access.worldpay.com/payments/settlements/refunds/full/{linkData}" \
  -u "username:password" \
  -H "Accept: application/vnd.worldpay.payments-v5+json" \
  -H "Content-Type: application/vnd.worldpay.payments-v5+json"
```

部分退款：

```bash
curl -X POST "https://try.access.worldpay.com/payments/settlements/refunds/partials/{linkData}" \
  -u "username:password" \
  -H "Accept: application/vnd.worldpay.payments-v5+json" \
  -H "Content-Type: application/vnd.worldpay.payments-v5+json" \
  -d '{
    "value": {
      "amount": 1000,
      "currency": "USD"
    },
    "reference": "ethoca_1E7543HNEG001VOURNV96Q857"
  }'
```

#### 6.4.4 Ethoca 反馈建议

* 若 Payment Queries 返回 `payments:refund` 或 `payments:partialRefund`，说明 PSP 当前提供可执行退款动作。
* 若交易尚未 settle，应优先 cancel/reversal，并反馈 `not settled`。
* Worldpay 产品线较多，本方案以 Access API 为基线；老 Corporate Gateway/XML 需单独评估。

### 6.5 Adyen

官方文档：

* Refund: <https://docs.adyen.com/online-payments/refund>
* Received payment details report: <https://docs.adyen.com/reporting/received-payment-details-report>
* Payment accounting report: <https://docs.adyen.com/reporting/payment-accounting-report>

#### 6.5.1 拉取交易

Adyen 标准 Online Payments 没有一个面向普通商户的实时“按日期列出全部支付”API。推荐组合：

1. Webhook 实时入库：`AUTHORISATION`、`CAPTURE`、`REFUND`、`REFUND_FAILED`、`CHARGEBACK`、`NOTIFICATION_OF_CHARGEBACK` 等。
2. Received payment details report：补齐授权交易明细、授权码、卡尾号、收单参考、网络交易参考。
3. Payment accounting report：做资金、退款、失败退款、冲正和结算对账。

#### 6.5.2 用于 Ethoca 匹配的字段

| Ethoca 字段  | Adyen 字段                                                |
| ---------- | ------------------------------------------------------- |
| 金额/币种      | `Amount`、`Currency`                                     |
| 交易时间       | `Creation Date`                                         |
| 卡尾号        | `Shopper PAN` 或 `Card Number`                           |
| 卡 BIN + 尾号 | `Issuer Id` 或 `Original Card Bin` 必须与卡尾号同时命中；BIN 不得单独使用 |
| 授权码        | `Authorisation Code`                                    |
| ARN/收单参考   | `Acquirer Reference`                                    |
| 3DS 信息     | `3D Directory Response`、`3D Authentication Response`    |
| 商户维度       | `Merchant Account`                                      |

`pspReference`、`merchantReference` 和 `Network Transaction Reference` 可用于客户内部查询、退款执行和审计，但不得与 Ethoca `transactionId` 或客户 order ID/reference 直接匹配。

#### 6.5.3 发起退款

```bash
curl -X POST "https://checkout-live.adyenpayments.com/checkout/v72/payments/PSP_REFERENCE/refunds" \
  -H "X-API-Key: ADYEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "merchantAccount": "YourMerchantAccount",
    "amount": {
      "value": 1000,
      "currency": "USD"
    },
    "reference": "ethoca_1E7543HNEG001VOURNV96Q857",
    "merchantRefundReason": "FRAUD"
  }'
```

退款结果异步确认。收到 response 后记录退款 `pspReference`，最终以 `REFUND`、`REFUND_FAILED`、`REFUNDED_REVERSED` webhook 或 Payment accounting report 为准。

#### 6.5.4 Ethoca 反馈建议

* Adyen 报表字段对 Ethoca 匹配很友好，但报表不是实时。建议 webhook 入库 + 报表补偿。
* 当 `Acquirer Reference` 已确认就是原交易 ARN 且完全一致时，视为完全匹配。ARN 缺失时，必须按第 4.2 节组合 `Authorisation Code`、前六后四或后四位、UTC 时间窗口和金额容差。
* 若 Adyen 返回退款校验失败，例如金额超过余额、未 capture、币种不一致，应转人工并反馈实际状态。

## 7. 自动化退款策略

### 7.1 默认策略

以下反馈必须按预警类型分别生成，禁止把斜杠表达式直接写入请求。

| 场景                                    | 系统动作         | Confirmed Fraud                                                                  | Customer Dispute                                                                   |
| ------------------------------------- | ------------ | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| 高置信唯一命中且通过全部退款校验                      | 按客户配置自动退款或审批 | 退款确认后 `stopped + refunded`                                                       | 退款确认后 `resolved + refunded`                                                        |
| 收到预警前已全额退款                            | 不重复退款        | 选择符合实际情况的 Confirmed Fraud 合法组合，并传 `matchTransactionResult=Refunded before alert` | `previously_refunded + refunded`，并传 `matchTransactionResult=Refunded before alert` |
| 授权未 capture 且已成功 void/cancel/reversal | 不发起 refund   | `previously_cancelled + not settled`                                             | `resolved + not settled`                                                           |
| 已正式拒付                                 | 禁止自动退款，转争议流程 | 按实际情况人工选择合法 outcome                                                              | `unresolved_dispute + not refunded`                                                |
| 找不到交易                                 | 转人工复核        | `notfound + not refunded`                                                        | `notfound + not refunded`                                                          |
| 多笔候选                                  | 转人工复核        | 可暂报 `in_progress`，必须在 24 小时内转最终态                                                 | 不得使用 `in_progress`；人工确认后发送 Customer Dispute 合法 outcome                             |
| 部分金额争议                                | 按客户金额策略处理    | 使用合法 outcome，`amountStopped` 填实际阻止/退款金额                                          | 使用 `resolved`/`other` 等合法 outcome，`amountStopped` 填实际部分退款金额                        |

### 7.2 金额策略

Ethoca FAQ 建议：如果确认是正确交易，为了更好阻止拒付，通常应全额退款，让发卡行看到全额退款。系统可支持三种客户配置：

| 策略     | 说明                                 | 适用场景        |
| ------ | ---------------------------------- | ----------- |
| 全额退款   | 按原交易可退余额退款                         | 默认推荐        |
| 预警金额退款 | 按 `chargebackAmount` 或 `amount` 退款 | 部分争议、订阅部分退款 |
| 人工确认金额 | 系统只给出建议金额                          | 高客单价、复杂履约   |

## 8. 上线前确认清单

以下项目尚不能仅凭现有原始文档唯一确定，必须通过 TrustDecision 服务端说明或 Sandbox 联调确认：

* [ ] 推送签名输出是十六进制还是 Base64，是否区分大小写。
* [ ] `timestamp` 允许偏差、过期策略和重放保护规则。
* [ ] 推送成功是否同时要求 HTTP `200` 和响应体 `code=200`。
* [ ] `alertTimestamp` 在两类预警中的真实类型，以及是否长期兼容毫秒时间戳和 ISO 8601。
* [ ] `ethocaMerchantId` 的稳定 JSON 类型。
* [ ] `comments` 是否接口必传，是否允许中文，以及 `other`/`Other` 时是否强制非空。
* [ ] `actionTimestamp` 的必传条件。
* [ ] 完整业务错误码、HTTP 状态码和可重试范围。
* [ ] 重复推送的重试间隔、最大次数和批量上限。
* [ ] Customer Dispute 对 Confirmed Fraud outcome 的兼容映射是否仍然有效。

上线前至少完成以下 Sandbox 用例：

* [ ] 同一 `ethocaId` 重复推送，确认接收端幂等且不会重复退款。
* [ ] 同一更新请求重复提交，确认客户端不会重复执行资金动作。
* [ ] Confirmed Fraud 和 Customer Dispute 各覆盖所有计划使用的合法 `outcome + refunded` 组合。
* [ ] 覆盖错误签名、过期时间戳、未知枚举、缺失必传字段和超长字段。
* [ ] 覆盖顶层 `code=200` 但 `alertUpdateResponses[].status=failed`。
* [ ] 覆盖 PSP 退款受理后失败、退款冲正和重复预警。

## 9. 参考资料

* TrustDecision Ethoca: <https://cn-doc.trustdecision.com/reference/ethoca.md>
* TrustDecision 支付信息对象: <https://cn-doc.trustdecision.com/reference/payment.md>
* TrustDecision 银行卡对象: <https://cn-doc.trustdecision.com/reference/card.md>
* TrustDecision 订单拒付事件: <https://cn-doc.trustdecision.com/reference/orderchargeback.md>
* Stripe PaymentIntents: <https://docs.stripe.com/api/payment_intents/list>
* Stripe Charges: <https://docs.stripe.com/api/charges/list>
* Stripe Refunds: <https://docs.stripe.com/api/refunds/create>
* Stripe Charge object: <https://docs.stripe.com/api/charges/object>
* Stripe Charge `authorization_code`: <https://docs.stripe.com/changelog/acacia/2024-09-30/displays-authorization-code-for-charges>
* Stripe Charge `network_transaction_id`: <https://docs.stripe.com/changelog/acacia/2024-12-18/charge-display-network-transaction-id>
* Stripe Refund references: <https://docs.stripe.com/refunds>
* Checkout.com Payment Search: <https://www.checkout.com/docs/payments/manage-payments/search-and-filter-payments>
* Checkout.com Payments API: <https://api-reference.checkout.com/tag/Payments/>
* CyberSource Transaction Search: <https://developer.cybersource.com/docs/cybs/en-us/txn-search/developer/all/rest/txn-search.html>
* Worldpay Payment Queries: <https://docs.worldpay.com/access/products/payment-queries>
* Worldpay Refund: <https://docs.worldpay.com/access/products/card-payments/v5/refund-a-payment>
* Adyen Refund: <https://docs.adyen.com/online-payments/refund>
* Adyen Received payment details report: <https://docs.adyen.com/reporting/received-payment-details-report>
* Adyen Payment accounting report: <https://docs.adyen.com/reporting/payment-accounting-report>