> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Merchant 直接集成 Apple Pay

> 在 Merchant 服务端解密 Apple Pay Token，并按 Waffo 指定格式透传解密结果。

Merchant 直接集成时，你需要使用 Apple Pay JS 或 PassKit 获取 Apple Pay Token，并通过服务端将 Token 提交给 Waffo。

你需要自行准备：

* Apple Developer 账号和 Merchant ID；
* Payment Processing Certificate 及对应私钥；
* Apple Pay on the Web 所需的域名验证；
* Apple Pay JS 或 PassKit 前端集成；
* Token 验签、解密、防重放检查和敏感数据合规措施。

开始前，请联系 Waffo 技术支持确认 Merchant 和支付方式配置。

## 当前支持：透传解密后的 Token

当前你必须先在 Merchant 服务端解密 Apple Pay Token，再按本页格式将解密结果传给 Waffo。

<Info>
  请求必须包含 `token.decryptedPaymentData`。`token.paymentData` 可以同时保留，但不能单独作为透传结果。
</Info>

## 原生加密 Token 结构

Apple Pay JS 或 PassKit 会返回下面的原生 payment token 信封。当前该对象是 Merchant 服务端执行验签和解密时的输入，不是可以直接提交给 Waffo 的 [`paymentTokenData`](/docs/api-reference/order-create/create-new-order#body-payment-token-data)。

```json theme={null}
{
  "billingContact": {
    "countryCode": "US",
    "givenName": "wz",
    "familyName": "w",
    "postalCode": "20001",
    "addressLines": ["dk", "dh"],
    "administrativeArea": "AL",
    "locality": "djj"
  },
  "token": {
    "paymentMethod": {
      "network": "MasterCard",
      "type": "credit",
      "displayName": "MasterCard 4444"
    },
    "transactionIdentifier": "e392617d9e2f7938ca727c6fd063dc7915e42f546bca810ea43f7c2759c52a26",
    "paymentData": {
      "data": "<encrypted_data>",
      "signature": "<signature>",
      "header": {
        "publicKeyHash": "<public_key_hash>",
        "ephemeralPublicKey": "<ephemeral_public_key>",
        "transactionId": "<transaction_id>"
      },
      "version": "EC_v1"
    }
  }
}
```

| 字段                            | 说明                                                                    |
| ----------------------------- | --------------------------------------------------------------------- |
| `token.paymentData`           | 加密数据本体，当前由 Merchant 使用证书私钥解密                                          |
| `token.paymentMethod.network` | 卡组，例如 `MasterCard` 或 `Visa`                                           |
| `billingContact`              | 账单联系人。Apple Pay JS 请求 `requiredBillingContactFields` 时返回，用于持卡人姓名和账单地址 |

## 解密和校验 Token

Apple Pay JS 或 PassKit 返回的 Token 中，`token.paymentData` 包含加密支付数据。你需要在服务端：

<Steps>
  <Step title="选择解密密钥">
    使用与 Payment Processing Certificate 匹配的私钥处理 Token。
  </Step>

  <Step title="验证 Token">
    按 Apple 的规范验证 Token 签名和证书链。
  </Step>

  <Step title="解密支付数据">
    按 Token 的 `version` 解密 `token.paymentData.data`，并将 UTF-8 结果解析为 JSON。
  </Step>

  <Step title="验证交易">
    确认 `transactionId` 未被处理过，并核对解密结果中的币种和金额。
  </Step>

  <Step title="组装 Waffo 请求">
    将解密后的 JSON 放入 `token.decryptedPaymentData`，再提交给 Waffo。
  </Step>
</Steps>

完整的密码学步骤和字段定义以 Apple 的 [Payment token format reference](https://developer.apple.com/documentation/PassKit/payment-token-format-reference) 为准。证书配置参见 Apple 的 [Setting up Apple Pay](https://developer.apple.com/documentation/PassKit/setting-up-apple-pay)。

<Warning>
  解密后的数据包含设备卡号和支付密文。不要在浏览器端解密，也不要记录完整 Token、设备卡号、私钥或支付密文。
</Warning>

## 解密后 payload 结构

`token.paymentData` 解密后得到 Apple 定义的 payment token payload：

```json theme={null}
{
  "applicationPrimaryAccountNumber": "5555555555554444",
  "applicationExpirationDate": "270831",
  "currencyCode": "156",
  "transactionAmount": 10,
  "deviceManufacturerIdentifier": "050110030273",
  "paymentDataType": "3DSecure",
  "paymentData": {
    "onlinePaymentCryptogram": "AORgiMGqVyeCAAt1LKSuAoABFA==",
    "eciIndicator": ""
  }
}
```

Subscription 支付的 MPAN 场景还会携带 `merchantTokenIdentifier` 等 Merchant Token 信息。

| 字段                                    | 说明                            |
| ------------------------------------- | ----------------------------- |
| `applicationPrimaryAccountNumber`     | 设备卡号（DPAN）                    |
| `applicationExpirationDate`           | 卡有效期，6 位数字，格式为 `YYMMDD`       |
| `paymentData.onlinePaymentCryptogram` | 3DS 密文，用于支付验证，可以为空            |
| `paymentDataType`                     | 支付数据类型：`3DSecure` 或 `EMV`     |
| 其余字段                                  | 设备标识、ECI、Merchant Token 等辅助信息 |

## 透传格式

只提交解密后的 payload 不够。解密后的数据不包含卡组 `network`，而且 Apple Pay 设备卡号的 BIN 通常无法反查卡组。账单联系人 `billingContact` 也位于加密数据之外，是持卡人姓名和账单地址的唯一来源。你必须保留 Apple Pay 返回对象的外层结构，并在 `token` 中增加 `token.decryptedPaymentData`。`token.paymentData` 可以同时保留。

下面的 JSON 是 [`/api/v1/order/create`](/docs/api-reference/order-create/create-new-order) 请求字段 [`paymentTokenData`](/docs/api-reference/order-create/create-new-order#body-payment-token-data) 的内容：

```json theme={null}
{
  "billingContact": {
    "countryCode": "US",
    "givenName": "wz",
    "familyName": "w",
    "postalCode": "20001",
    "addressLines": ["dk", "dh"],
    "administrativeArea": "AL",
    "locality": "djj"
  },
  "token": {
    "paymentMethod": {
      "network": "MasterCard",
      "type": "credit",
      "displayName": "MasterCard 4444"
    },
    "transactionIdentifier": "e392617d9e2f7938ca727c6fd063dc7915e42f546bca810ea43f7c2759c52a26",
    "decryptedPaymentData": {
      "applicationPrimaryAccountNumber": "5555555555554444",
      "applicationExpirationDate": "270831",
      "currencyCode": "156",
      "transactionAmount": 10,
      "deviceManufacturerIdentifier": "050110030273",
      "paymentDataType": "3DSecure",
      "paymentData": {
        "onlinePaymentCryptogram": "AORgiMGqVyeCAAt1LKSuAoABFA==",
        "eciIndicator": ""
      }
    }
  }
}
```

[`paymentTokenData`](/docs/api-reference/order-create/create-new-order#body-payment-token-data) 在创建订单 API 中是 `String`。提交请求前，将上述完整对象序列化为 JSON 字符串：

```typescript theme={null}
const paymentTokenData = JSON.stringify(decryptedApplePayPayload);
```

Waffo 检测到 `token.decryptedPaymentData` 时，会优先按解密后 Token 处理并跳过平台解密。

## 字段要求

| 字段                                                               | 要求   | 说明                                                                                                 |
| ---------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------------------- |
| `token.decryptedPaymentData.applicationPrimaryAccountNumber`     | 必填   | 设备卡号（DPAN）                                                                                         |
| `token.decryptedPaymentData.applicationExpirationDate`           | 必填   | 6 位数字，格式为 `YYMMDD`                                                                                 |
| `token.paymentMethod.network`                                    | 必填   | 从原始 Token 外层结构原样透传的卡组                                                                              |
| `billingContact`                                                 | 建议透传 | 持卡人姓名和账单地址的来源；Apple Pay 有返回时应完整透传                                                                  |
| `token.decryptedPaymentData.paymentData.onlinePaymentCryptogram` | 可选   | 3DS 支付密文；有值时原样透传                                                                                   |
| `token.decryptedPaymentData` 内其他字段                               | 可选   | `paymentDataType`、`deviceManufacturerIdentifier`、`eciIndicator`、`merchantTokenIdentifier` 等有值时原样透传 |

<Note>
  只有 Apple Pay 支持解密后 Token 透传。Google Pay Token 必须以加密形式提交。
</Note>
