> 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/bao-cun-ka-xin-xi-gong-neng.md).

# 保存卡信息功能

## **概述**

Codapay 支持为回访用户保存卡信息，以简化并加快结账流程。该功能适用于符合条件的商户和已启用的卡组织，且仅在支付页面（Hosted Payment Page）API 上提供。

启用该功能后，用户可在结账时安全地保存卡信息，并在后续交易中复用。

> ⚠️ 注意：此功能目前不适用于以下国家：印度、印度尼西亚，韩国。

## 功能启用条件

保存卡信息功能仅对已开通该功能的商户开放。

如需启用此功能，请联系您的 Codapay 客户经理或支持团队。

若商户未开通该功能，支付仍会正常处理，但卡信息不会被保存。此类情况下，系统会在 `/v2.0/Payment/init.json` 接口响应中返回一条警告信息。

## 用户识别与 Shopper 映射

在发起涉及保存卡信息或使用已保存卡的支付前，用户必须已登录贵方平台。

您需在调用 `/v2.0/Payment/init.json` 接口时传入 `partner_shopper_id`，此字段表示您系统中的用户唯一标识，需满足以下要求：

* 唯一标识单一用户
* 跨设备及会话保持一致
* 在所有与保存卡相关的请求中均需传入

当用户首次保存卡信息时，Codapay 会生成一个 `shopper_id`，并将其与提供的 `partner_shopper_id` 建立映射关系。此后所有保存的卡片均归属于该 `shopper_id`。

Codapay 通过该映射实现以下功能：

* 在后续结账流程中获取用户保存的卡片
* 将新保存的卡片正确归属到对应用户
* 在无需用户额外操作的情况下处理自动续费或商户发起的扣款

## 接入概览

如需在支付页面（Hosted Payment Page）API 启用保存卡信息功能，请在调用 `/v2.0/Payment/init.json` 接口时包含以下字段：

<table><thead><tr><th width="295.77001953125">字段名</th><th width="94.802978515625">类型</th><th>描述</th></tr></thead><tbody><tr><td><code>enableSavePaymentMethod</code></td><td>Boolean</td><td>是否允许用户保存卡信息</td></tr><tr><td><code>displaySavedPaymentMethodList</code></td><td>Boolean</td><td>是否展示用户此前保存的卡信息</td></tr><tr><td><code>userInitiated</code></td><td>Boolean</td><td>是否为用户主动发起的支付流程</td></tr><tr><td><code>shopper</code></td><td>Object</td><td>用户身份信息及账单详情，用于生成卡片令牌（token）</td></tr></tbody></table>

## Shopper 对象说明

在 `/v2.0/Payment/init.json` 请求中，`shopper` 字段为一个包含多组键值对的对象，格式为 `shopper.entry[]`。

每组 key-value 对应用户的身份信息或账单信息，用于：

* 创建或获取 Codapay 中的 shopper profile
* 支持保存卡信息功能（如保存与调用用户卡片）

<table><thead><tr><th width="227.6632080078125">字段名</th><th width="119.3385009765625">必填？</th><th>描述</th></tr></thead><tbody><tr><td><code>partnerShopperId</code></td><td><strong>是</strong></td><td>来自您系统的唯一用户 ID（区分大小写）</td></tr><tr><td><code>id</code></td><td>否</td><td>Codapay 分配的 <code>shopper_id</code>，用于检索已存在的用户</td></tr><tr><td><code>email</code></td><td>否</td><td>用户邮箱（创建新 shopper 时必填）</td></tr><tr><td><code>phoneNumber</code></td><td>否</td><td>用户手机号</td></tr><tr><td><code>billingFirstName</code></td><td>否</td><td>用户账单中的名（创建 billing 信息和 shopper 时必填）</td></tr><tr><td><code>billingLastName</code></td><td>否</td><td>用户账单中的姓（创建 billing 信息和 shopper 时必填）</td></tr><tr><td><code>billingDocumentType</code></td><td>条件必填</td><td>国家身份证件类型（如 CPF），某些市场为必填项</td></tr><tr><td><code>billingDocumentId</code></td><td>条件必填</td><td>身份证件号码，与 <code>billingDocumentType</code> 搭配使用</td></tr><tr><td><code>billingAddress</code></td><td>否</td><td>街道地址</td></tr><tr><td><code>billingCity</code></td><td>否</td><td>城市名称</td></tr><tr><td><code>billingRegion</code></td><td>否</td><td>州或省份</td></tr><tr><td><code>billingCountry</code></td><td>否</td><td>国家代码（ISO alpha-2 格式，如 US）</td></tr><tr><td><code>billingZipCode</code></td><td>否</td><td>邮政编码</td></tr></tbody></table>

> **首次保存卡信息时为必填项**。

* 若提供了 `id`（Codapay 分配的 `shopper_id`），系统将尝试检索对应的现有用户。
* 若未提供 `id`，Codapay 将根据提供的 `partnerShopperId` 创建新的 shopper profile 并建立映射关系。
* 此处列出的字段为**保存卡信息所需的附加字段**，需在原有标准字段基础上补充。

请参考下方 “使用场景与示例” 部分获取完整的请求结构示例。

## 使用场景与示例

本节介绍接入保存卡信息功能时最常见的使用场景。每个场景均包含：

* 所需参数说明
* 对应的请求示例格式（JSON）

通过这些示例，您可以参考如何构造请求以实现保存卡、调用已保存卡、或进行自动扣款等功能。

### 场景 1：首次用户 – 保存卡信息

允许用户在结账时保存卡信息

![](https://lh7-rt.googleusercontent.com/slidesz/AGV_vUcR11xjyCtJfxQ5bTcAiSf8jqj7Xq7zd5QmG2JCJ6kE3WRcsHHJRBYBBugn4tVnGYenj3O_YkDrQFH8kVLZwlEoBnnEArkE_JiZoYy_Qmwht5OmanUE0CUprPQl0HqKJnIUO8MFCA=s2048?key=gGON4PQ0VryRSm6zbKD4uA)

#### 请求

```json
{
  "initRequest": {
    "country": 360,
    "payType": 400,
    "apiKey": "",
    "projectId": "31",
    "orderId": "20250611113402",
    "currency": 360,
    "items": [
      {
        "code": "12ds",
        "price": 10000.00,
        "name": "Star Diamond"
      }
    ],
    "profile": {
      "entry": [
        {
          "key": "user_id",
          "value": "105"
        }
      ]
    },
    "enableSavePaymentMethod": true,
    "displaySavedPaymentMethodList": true,
    "userInitiated": true,
    "shopper": {
      "entry": [
        {
          "key": "partnerShopperId",
          "value": "test_partner_123_Id"
        },
        {
          "key": "email",
          "value": "test@example.com"
        }
      ]
    }
  }
}
```

#### 响应

```json
{
  "initResult": {
    "resultCode": 0,
    "txnId": 7503012627171645973
  }
}
```

#### 商户通知（Merchant Notification）

```json
{
  "eventType": "PAYMENT_METHOD_ADDED",
  "timestamp": "2025-06-19T02:48:39.568907Z",
  "shopper": {
    "id": "shp_sg197861636b40021",
    "partner_shopper_id": "test_partner_123_Id",
    "email": "test@example.com"
  },
  "paymentMethod": {
    "id": "pmt_sg1978616a1900024",
    "type": "card",
    "card": {
      "lastFour": "7263",
      "expirationMonth": "04",
      "expirationYear": "2027"
    }
  }
}
```

### 场景 2：回访用户 – 显示已保存卡片

向用户展示其已保存的卡片供其选择使用。

![](https://lh7-rt.googleusercontent.com/slidesz/AGV_vUdEAGAY5Zx2GS16XZ0bw30Q9tSX35CLiL6M2JdqGjnrZjyRCwsYRJAKsu0sj6_1SvG4nVrJM4ou-cSJwm4YgROv6i47bCv-HlXYzLYmpWOyyjnhQUDBVmYJUMtT0xFuQ-QveiqUwQ=s2048?key=gGON4PQ0VryRSm6zbKD4uA)

#### 请求

```json
{
  "initRequest": {
    "country": 360,
    "payType": 400,
    "apiKey": "",
    "projectId": "31",
    "orderId": "20250611113402",
    "currency": 360,
    "items": [
      {
        "code": "12ds",
        "price": 10000.00,
        "name": "Star Diamond"
      }
    ],
    "profile": {
      "entry": [
        {
          "key": "user_id",
          "value": "105"
        }
      ]
    },
    "enableSavePaymentMethod": true,
    "displaySavedPaymentMethodList": true,
    "userInitiated": true,
    "shopper": {
      "entry": [
        {
          "key": "id",
          "value": "shp_sg197861636b40021"
        }
      ]
    }
  }
}
```

#### 响应

```json
{
  "initResult": {
    "resultCode": 0,
    "txnId": 7503016855911597963
  }
}
```

### 场景 3：回访用户 – 无界面直接扣款

即将推出

### 场景 4：商户发起支付 – 用户不在场

即将推出

## 通知说明

当卡信息成功保存时，Codapay 会实时向您配置的 webhook 接口发送通知。

这些功能使您能够跟踪并核对已保存的卡片交易记录与您的用户记录。

### **`PAYMENT_METHOD_ADDED`**

当新卡信息成功保存并关联到 shopper profile 时触发该通知。

### **通知负载**

```json
{
  "eventType": "PAYMENT_METHOD_ADDED",
  "timestamp": "2025-06-19T02:48:39.568907Z",
  "shopper": {
    "id": "shp_sg197861636b40021",
    "partner_shopper_id": "test_partner_123_Id",
    "email": "test@example.com"
  },
  "paymentMethod": {
    "id": "pmt_sg1978616a1900024",
    "type": "card",
    "card": {
      "lastFour": "7263",
      "expirationMonth": "04",
      "expirationYear": "2027"
    }
  }
}
```

### **字段详细信息**

| 字段名                                  | 描述                              |
| ------------------------------------ | ------------------------------- |
| `eventType`                          | 活动类型，固定为 `PAYMENT_METHOD_ADDED` |
| `timestamp`                          | 保存支付方式的时间                       |
| `shopper.id`                         | Codapay 分配的 shopper ID          |
| `shopper.partner_shopper_id`         | 在初始化时传递的商户端用户 ID                |
| `shopper.email`                      | 用户邮箱                            |
| `paymentMethod.id`                   | Codapay 内部生成的卡片 ID              |
| `paymentMethod.type`                 | 支付方式类型，例如 `card`                |
| `paymentMethod.card.lastFour`        | 卡号后四位                           |
| `paymentMethod.card.expirationMonth` | 卡到期月份（MM）                       |
| `paymentMethod.card.expirationYear`  | 卡到期年份（YYYY）                     |

* Codapay 需接收到 **2xx HTTP 响应码**，以标记通知已成功接收。
* 若收到非 2xx 响应，Codapay 将自动重试发送该通知。
* 可通过 `shopper.id` 与您内部的用户 ID 进行事件匹配。
* 每条通知均附带签名，**您必须验证 checksum** 以确保通知来源可靠。\
  详细实现请参阅[校验和计算步骤](https://coda-payments.gitbook.io/zhong-wen-coda-private-technical-documentation/codapay/ji-cheng-zhi-nan/an-quan-zhi-nan#jiao-yan-he-ji-suan-bu-zhou)**部分**。


---

# 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/bao-cun-ka-xin-xi-gong-neng.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.
