> 生产主站和 API 使用 https://rockpay.ai，托管结账使用 https://checkout.rockpay.ai。既有 shortpay.ai、checkout.shortpay.ai 和已配置的旧域名仍可直接访问；新接入使用 RockPay 域名。

# RockPay 公共 API 文档

RockPay 提供**一次性支付 API**。本文公开可读；业务 API 仍要求在受信任服务端使用本站点 Bearer 密钥。不提供浏览器密钥、订阅、跨账户代理、结算或任意资源列表。

- 人类文档：`https://rockpay.ai/docs`
- 原始 Markdown：`https://rockpay.ai/v1/docs?format=markdown`
- 机器目录：`https://rockpay.ai/v1/docs?format=catalog`
- 集成 skill：`https://rockpay.ai/v1/docs?format=integration-skill`
- 数据读取 skill：`https://rockpay.ai/v1/docs?format=data-skill`

生产服务地址：网站、公共文档与管理页面使用 `https://rockpay.ai`；服务端 API base 使用 `https://rockpay.ai`；托管结账、同源 `/checkout-api/*` 与 `/embed.js` 使用 `https://checkout.rockpay.ai`。管理浏览器请求保持与 `https://rockpay.ai` 同源。服务端 API 与网站、公共文档共用主域名。

## 快速开始

服务端创建 Checkout Session，随后只把响应中的 `url` 交给购买者：

```bash
export SHORTPAY_API_BASE='https://rockpay.ai'
export SHORTPAY_SITE_KEY='<SITE_SERVER_KEY>'

curl -X POST "$SHORTPAY_API_BASE/v1/checkout/sessions" \
  -H "Authorization: Bearer $SHORTPAY_SITE_KEY" \
  -H 'Idempotency-Key: order-2026-0001' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'mode=payment' \
  --data-urlencode 'line_items[0][price_data][currency]=usd' \
  --data-urlencode 'line_items[0][price_data][unit_amount]=1200' \
  --data-urlencode 'line_items[0][price_data][product_data][name]=Example credits' \
  --data-urlencode 'line_items[0][quantity]=1' \
  --data-urlencode 'customer_email=buyer@example.com' \
  --data-urlencode 'success_url=https://merchant.example/order/complete?session_id={CHECKOUT_SESSION_ID}' \
  --data-urlencode 'cancel_url=https://merchant.example/order/cancelled'
```

典型响应（字段会因资源状态变化）：

```json
{"id":"cs_example","object":"checkout.session","mode":"payment","status":"open","payment_status":"unpaid","amount_total":1200,"currency":"usd","payment_intent":"pi_example","url":"https://checkout.rockpay.ai/checkout/cs_example?token=<SHORT_LIVED_CAPABILITY>"}
```

`url` 是短期购买者能力链接，不是 API 密钥：只交给已验证的购买者；不要写入日志、分析系统或长期存储。回跳、嵌入页消息和浏览器轮询都不是付款确认。履约必须来自已验证的事件或服务端读取的确认状态。

## 认证、版本与通用规则

- 所有 `/v1/...` 业务端点均要求 `Authorization: Bearer <SITE_SERVER_KEY>`；只接受恰好对应一个站点的服务端密钥。缺失或无效密钥为 `401`，非站点身份为 `403`。
- 固定兼容版本为 `2025-03-31.basil`。可省略 `ShortPay-Version`；若发送，必须完全相同。每个成功或错误响应都会带回该版本头。
- 写操作使用 `application/x-www-form-urlencoded`。GET 不接受未列出的查询参数。表单最大 64 KiB，重复参数、冲突嵌套结构、非法编码和未支持字段都会被拒绝。
- 金额为最小货币单位的十进制整数；创建 Session 的 `currency` 必须为小写，且一个订单只能有一种货币。
- 成功读写均返回 RockPay JSON 对象。错误统一为：

```json
{"error":{"type":"invalid_request_error","message":"Invalid or unsupported parameter","param":"field","code":"parameter_invalid"}}
```

常见状态：`400` 参数/版本/状态无效，`401` 认证失败，`403` 站点权限不足，`404` 资源不存在或不属于本站点，`409` 幂等键与请求体不一致，`413` 请求过大，`503` 当前退款能力不可用。

### 幂等性

写请求可带 `Idempotency-Key`（1–200 个可打印 ASCII 字符）。键按“站点 + 操作”隔离：相同键和完全相同表单会返回第一次保存的对象；相同键配不同参数返回 `409` / `idempotency_key_in_use`。网络超时后必须重用同一键和同一请求，不能新建订单或退款。

## 公共 RockPay API

### Account 与收入读取

| 方法 | 路径 | 用途 |
|---|---|---|
| GET | `/v1/account` | 返回稳定的本站点 account 对象；`charges_enabled`、`payouts_enabled` 始终为 `null`（未知）。 |
| GET | `/v1/charges` | 返回已确认收款的 Charge 列表。 |
| GET | `/v1/events` | 返回不可变的 `charge.succeeded` 收入快照列表，不是 webhook 投递列表。 |

`/v1/charges` 和 `/v1/events` 支持 `limit`（1–100，默认 10）、互斥的 `starting_after` / `ending_before`，以及 `created` 精确 Unix 秒或 `created[gt]`、`[gte]`、`[lt]`、`[lte]`。结果为最新优先的 `{ "object":"list", "data":[], "has_more":false }`。Events 只接受可选的单一 `types[]=charge.succeeded`。游标必须属于当前站点和当前集合。

Account 对象恰含 `id`、`object:"account"`、`charges_enabled:null`、`payouts_enabled:null`；不接受查询参数。

Charge 对象包含 `id`、`object:"charge"`、`created`、`amount`、`currency`、`paid:true`、`status:"succeeded"`、`customer`、`billing_details.email`、`receipt_email:null`、`refunded`、`amount_refunded`、`livemode`。`created` 是首次验证支付成功的时间；Customer/邮箱可能为空。`refunded` 只有全额退款才为 true，部分退款仍须读取 `amount_refunded`。

收入 Event 包含 `id`、`object:"event"`、`api_version`、`created`、`type:"charge.succeeded"`、`livemode`、`data.object`（首次确认的 Charge 快照）、`pending_webhooks:0`、`request:null`。它不是 webhook 投递历史，不会为此生成新的商户履约通知。列表还返回 `url`，分别为 `/v1/charges` 或 `/v1/events`。

Charge 反映已确认付款；`amount_refunded` 只统计已确认退款。Event 是首次确认时的不可变快照，之后的退款不会改写它。两者都不是结算、打款或银行对账证据。

`types[0]=charge.succeeded` 也可用。`created` 不与范围条件混用，`gt/gte` 及 `lt/lte` 各自互斥，时间范围必须有效。Charge 按确认时间和稳定顺序倒序，Event 按记录顺序倒序；时间过滤分别针对各自的 `created`。`starting_after` 向后翻页，`ending_before` 向前翻页，返回页内仍保持倒序。

### Customers

| 方法 | 路径 |
|---|---|
| POST | `/v1/customers` |
| GET | `/v1/customers/{customer_id}` |
| POST | `/v1/customers/{customer_id}` |

创建和更新只接受以下表单字段：

| 字段 | 说明 |
|---|---|
| `email` | 可为空以清除；非空时必须是有效邮箱。 |
| `name` | 可为空以清除；最长 500 字符。 |
| `address[country]` | 可选 ISO 3166-1 alpha-2 国家码。 |
| `address[postal_code]`、`line1`、`line2`、`city`、`state` | 可选地址字符串。 |
| `metadata[key]` | 最多 50 个字符串值；键最长 40 字符。传空 `metadata` 清空全部；空值删除单个键。 |

Customer 响应包含 `id`、`object:"customer"`、`created`、`livemode`、`email`、`name`、`address` 和 `metadata`。不支持 Customer 列表、删除或任意字段扩展。

### Checkout Sessions

| 方法 | 路径 |
|---|---|
| POST | `/v1/checkout/sessions` |
| GET | `/v1/checkout/sessions/{session_id}` |
| GET | `/v1/checkout/sessions/{session_id}/line_items` |
| POST | `/v1/checkout/sessions/{session_id}/expire` |

创建 Session 必须有：

| 字段 | 规则 |
|---|---|
| `mode` | 必须为 `payment`。 |
| `line_items[N][price_data][currency]` | 小写、受支持货币；所有条目一致。 |
| `line_items[N][price_data][unit_amount]` | 最小单位整数，0 至 1,000,000,000。 |
| `line_items[N][price_data][product_data][name]` | 商品名；不支持 `#`、`&`、`+`。 |
| `line_items[N][quantity]` | 正整数。 |
| `success_url`、`cancel_url` | 必填 HTTPS 回跳地址，origin 必须属于站点预配置允许来源；无凭据、无 fragment。 |

可选字段：`customer`（已有 Customer ID）、`customer_email`、`customer_creation`（`if_required` 或 `always`）、`customer_update[address]`（`auto` 或 `never`）、`client_reference_id` 和 `metadata`。`customer_update[address]` 仅可与 `customer` 一起使用；`customer` 不能与 `customer_email` 或 `customer_creation` 同时使用。Session 最多 100 个连续编号的条目（从 `0` 开始）。总金额必须在 1 至 1,000,000,000。

**接入要求（先改业务侧，尚非 API 校验）**：新建支付 Session 的业务后端应提交当前购买者的有效 `customer_email`。RockPay 当前仍接受不带该字段的请求；在所有业务站完成改造和验证之前不会启用必填校验。已有 `customer` 与 `customer_email` 仍不能同传，使用仅传 `customer` 的业务方须先迁移该调用。邮箱用于付款联系/账单资料，不代替业务侧的订单和用户归属校验。顺序是先完成业务侧改造并验证，再另行启用 RockPay 必填校验。

Session 对象包含：`id`、`object`、`mode`、`created`、`expires_at`、`livemode`、`status`、`payment_status`、`amount_subtotal`、`amount_total`、`currency`、`customer`、`customer_email`、`customer_creation`、`customer_details`、`client_reference_id`、`metadata`、`payment_intent`、`success_url`、`cancel_url`、`url`。GET Session 仅可用 `expand[]=line_items` 或 `expand[0]=line_items` 展开条目；完整条目也可使用专用 line-items 路径读取。

`POST .../expire` 发送空表单。它只能使 `open` Session 过期，返回 `status:"expired"` 且 `url:null`。它不取消已经开始的付款；晚到的已确认付款仍可成功。

### PaymentIntents 与 Refunds

| 方法 | 路径 |
|---|---|
| GET | `/v1/payment_intents/{intent_id}` |
| POST | `/v1/refunds` |
| GET | `/v1/refunds/{refund_id}` |

PaymentIntent 包含 `id`、`amount`、`amount_received`、`currency`、`customer`、`metadata`、`status`。状态如 `requires_payment_method`、`processing` 并不代表成功；只有已确认结果才可履约。

创建 Refund 的表单：

| 字段 | 规则 |
|---|---|
| `payment_intent` | 必填，必须属于当前站点。 |
| `amount` | 可选正整数；省略时申请当前未保留余额。 |
| `reason` | 可选：`duplicate`、`fraudulent`、`requested_by_customer`。 |
| `metadata[key]` | 可选，遵循 Customer metadata 规则。 |

Refund 返回 `id`、`object:"refund"`、`created`、`amount`、`currency`、`payment_intent`、`status`、`reason`、`metadata`。`pending`、`failed` 或未知结果都不是完成退款；不要以新请求重试模糊结果。确认成功后再调整权益。

## 托管直接卡片结账

买家仅通过 Session `url` 打开 `/checkout/{session_id}?token=...`。页面在受控同源请求中向 `/checkout-api/{session_id}/pay-card` 提交卡片信息；卡号与 CVC 不应由商户服务器、日志或分析工具接收。请求还需 Session capability、精确同源 `Origin` 和 JSON body：`email`、`name`、`billing_address`、`os_type`、`card`。服务端派生金额和账户，单个 Session 只允许一次尝试。

支付结果只能作为页面显示：`open` / `pending` / `unknown` 不是付款成功；可能的认证跳转也不是成功证明。商户不要实现自己的前端卡片提交，也不要从浏览器回调履约。

`GET https://checkout.rockpay.ai/embed.js` 提供 `ShortPay.open({ checkoutUrl, onStatus, onClose })` 作为托管页面的可选展示工具。`checkoutUrl` 必须是未修改的 RockPay Session URL；`onStatus` 仅用于显示，不能授权发货。

## 商户通知与履约

在站点配置中填写你控制的 HTTPS `event_url`，并在服务端保存该站点的 `event_secret`；接收路径由商户自行决定，RockPay 不要求固定路径。`event_url` 是服务端通知入口，与浏览器 `return_url` 不同。RockPay 向它发送 `POST`、`Content-Type: application/json` 的事件；不跟随重定向。站点配置和凭据由授权管理员管理，见[认证管理参考](https://rockpay.ai/v1/docs?format=management-reference)。不要将 `event_secret` 放入前端。

确认付款产生 `payment_intent.succeeded`；Session 结账完成且未提前过期时，同一笔付款还产生 `checkout.session.completed`。显式过期产生 `checkout.session.expired`；过期后晚到的已确认付款仍可能产生 `payment_intent.succeeded`，不能仅按 Session 过期拒绝真实付款。确认退款产生 `refund.updated`，失败产生 `refund.failed`。这些是商户 webhook 事件；`GET /v1/events` 返回的 `charge.succeeded` 是独立的收入快照，不是投递清单。

支持失败通知的支付通道在可信服务端通知或原单查询确认失败后，还会产生 `payment_intent.payment_failed`；`data.object` 是原 PaymentIntent，`status=requires_payment_method`、`payment_failure_confirmed=true`、`amount_received=0`。浏览器报错、取消和未决状态不会产生此失败事件。接收端验签并核对本站原订单、金额和货币后，应服务端重新读取原 Session / PaymentIntent 再同步失败，不发放权益、不自动重付，也不得降级已确认成功的订单。新的购买只由用户主动发起，先确认原单可安全重建并使用新业务订单和幂等键；该事件不会自动补发历史失败记录。

签名头为 `ShortPay-Signature: t=<unix-seconds>,v1=<lowercase-hex>`。在解析 JSON **之前**，以该站点 `event_secret` 对 `timestamp + "." + raw_request_body_bytes` 计算 HMAC-SHA256，常量时间比较，并拒绝超出接收端时间窗的时间戳；不要重新序列化 JSON 后验签。每次重投重新生成时间戳和签名，事件 ID 与原始事件 body 不变。[集成指南](https://rockpay.ai/v1/docs?format=integration-skill)提供可运行的验签示例。旧 `/management` 事件使用另一组 `X-ShortPay-*` 头与不同对象格式，不能混用。

以下是字段节选示例，省略了 `data.object` 中其余实际字段；完整对象以对应资源的 API 契约为准：

```json
{"id":"evt_example","object":"event","api_version":"2025-03-31.basil","created":1760000000,"livemode":true,"type":"payment_intent.succeeded","data":{"object":{"id":"pi_example","object":"payment_intent","status":"succeeded","amount":1200,"amount_received":1200,"currency":"usd"}},"pending_webhooks":1,"request":null}
```

```json
{"id":"evt_example2","object":"event","api_version":"2025-03-31.basil","created":1760000001,"livemode":true,"type":"checkout.session.completed","data":{"object":{"id":"cs_example","object":"checkout.session","status":"complete","payment_status":"paid","amount_total":1200,"currency":"usd","payment_intent":"pi_example","client_reference_id":"merchant-order-123"}},"pending_webhooks":1,"request":null}
```

```json
{"id":"evt_example3","object":"event","api_version":"2025-03-31.basil","created":1760000002,"livemode":true,"type":"refund.updated","data":{"object":{"id":"rfd_example","object":"refund","status":"succeeded","amount":200,"currency":"usd","payment_intent":"pi_example"}},"pending_webhooks":1,"request":null}
```

`pending_webhooks` 是事件创建时的快照值，不能当作当前投递状态。对 `payment_intent.succeeded` 使用 `data.object.id` 绑定 PaymentIntent；对 `checkout.session.completed` 使用 `data.object.payment_intent` 绑定同一付款，并确认 `payment_status=paid`。核对本地订单、金额、货币和站点归属；两个事件或服务端主动查询都可能确认同一付款，必须共用按付款/订单 ID 唯一的履约事务。退款按 Refund ID 去重，并核对原付款及退款金额，只在 `refund.updated` 的 `data.object.status=succeeded` 后调整权益。`refund.failed` 不能当作成功。

商户只有在验签、核对并持久化（或可靠入队）后才返回任意 HTTP 2xx；其他状态或网络错误会触发重投。当前投递超时 10 秒，最多尝试 10 次，首次失败约 30 秒后重试，此后指数退避、最长一小时；耗尽后成为未自动投递的 dead 事件。接收端应快速确认并异步处理耗时工作，且重复事件不得重复履约。授权方可按已知事件 ID 通过 `GET /management/events/{id}/delivery` 查询投递、`POST /management/events/{id}/retry` 请求补送未投递事件；先修复接收端并核对幂等，避免盲目补发。该管理接口不是公开 `/v1/events` 列表；若缺少事件 ID，用保存的 Session/PaymentIntent ID 主动查询支付结果并走相同履约事务。

## 认证管理参考（不公开 API）

以下是完整部署中的认证管理面，**不是**公共 RockPay 协议；仍要求现有 Bearer/Cookie/CSRF/站点范围认证，本文不提供敏感凭据、回调或部署资料。

- `/management/...`：历史客户、支付、退款、发票、事件投递与对账操作。
- `/admin/...`：管理登录会话。
- `/v1/merchants`、`/v1/sites` 和凭据管理：受控目录与管理资源。
- `/checkout-api/...`：由短期买家 capability 保护的托管结账内部接口。

不要把“文档公开”误解成上述端点可匿名访问。请使用 API base URL 上的真实认证策略和最小站点范围。

## 不支持的能力

订阅/循环扣款、保存卡、授权捕获、浏览器支付 SDK、跨账户代理、Product/Price 管理、税/运费/折扣、任意 `expand`、任意分页资源、结算报告与 read-only API key 均不在此协议中。未列字段和端点不应被推测为可用。

## 主动查询支付结果

在业务站服务端调用以下任一接口，使用创建 Session 时返回的 `id`（`cs_...`）或 `payment_intent`（`pi_...`）。业务站自己的订单号不能代替这两个 ID。

两个接口均使用本站点服务端 Bearer 密钥。存在支付尝试时，每次读取都会主动核验原支付结果并同步已确认状态，包括此前已成功的付款；尚未发起支付时，返回未支付对象，不发起扣款。

请求示例：

```bash
curl 'https://rockpay.ai/v1/checkout/sessions/cs_example' \
  -H "Authorization: Bearer $SHORTPAY_SITE_KEY"

curl 'https://rockpay.ai/v1/payment_intents/pi_example' \
  -H "Authorization: Bearer $SHORTPAY_SITE_KEY"
```

成功响应示例（仅展示核心字段）：

```json
{"id":"cs_example","object":"checkout.session","status":"complete","payment_status":"paid","amount_total":1200,"currency":"usd","payment_intent":"pi_example"}
```

```json
{"id":"pi_example","object":"payment_intent","status":"succeeded","amount":1200,"amount_received":1200,"currency":"usd"}
```

判断付款成功：Session 的 `payment_status` 为 `paid`，或 PaymentIntent 的 `status` 为 `succeeded`。不要仅凭 Session 的 `status` 判断付款成功；其值为 `open`、`complete` 或 `expired`。

未成功时，Session 的 `payment_status` 为 `unpaid`；PaymentIntent 返回 `processing`（处理中）或 `requires_payment_method`（尚未提交或已确认失败）。响应金额使用整数最小货币单位，货币为小写代码。

查询超时、查询服务不可用、结果无法验证或暂时无法确认最终性时，返回 `502` / `api_error`，不是未支付或付款失败。错误响应：

```json
{"error":{"type":"api_error","message":"Payment inquiry unavailable or unverified","param":null,"code":null}}
```

稍后使用同一 ID 重试；不要因查询失败重新创建支付或扣款。查询确认成功后，核对金额、币种及本地订单归属，并与 Webhook 共用按支付 ID 去重的履约逻辑，防止重复发放。

本接口无需请求体或刷新参数。PaymentIntent 查询不接受查询参数；Session 仅保留 `expand[]=line_items` 或 `expand[0]=line_items`。密钥无效返回 `401`，非站点身份返回 `403`，对象不存在或不属于本站点返回 `404`。
