## 6. API Documentation(托管收银台模式) 托管收银台模式是 Infini 最推荐的接入方式。商户只需创建订单、跳转 checkout_url 并处理 Webhook,即可完成支付集成。本章节仅包含托管收银台模式的 API 文档与对应字段说明。 所有接口前缀: `/v1/acquiring` ### 6.1 创建订单(Create Order) **POST** /v1/acquiring/order 用于创建订单,并返回托管收银台的访问 URL (checkout_url)。 #### Headers ```json Content-Type: application/json Date: {GMT Time} Authorization: Signature ... ``` #### Request Body | 字段 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | amount | string/number | 是 | 订单法币金额(最多 6 位小数) | | currency | string | 是 | 法币,如 "USD" | | request_id | string | 是 | 商户自定义生成的幂等key, UUID "a759b99a-9d22-433d-bced-ab1d2e1bea1d" | | client_reference | string | 否 | 商户自定义订单号,建议保持唯一 | | description | string | 否 | 订单描述 | | expires_in | number | 否 | 订单过期相对时间(Unix 秒);不传则使用后台默认值 | | merchant_alias | string | 否 | 收银台账单名称(覆盖后台配置) | | success_url | string | 否 | 订单支付成功跳转回商户地址 | | failure_url | string | 否 | 订单支付失败跳转回商户地址 | #### Response 示例 ```json { "order_id": "10290d05-xxxx", "amount": "100", "currency": "USD", "checkout_url": "https://checkout.infini.money/pay/xxxx", "expires_in": 3600 } ``` ### 6.2 查询订单(Query Order) **GET** /v1/acquiring/order?order_id ={order_id} 返回订单的实时状态信息。 #### Order Status 字段说明 ##### `status` - 支付进度状态 实时计算字段,反映订单当前的支付进度(基于 `amount_confirmed` vs `order_amount`)。 | 值 | 说明 | | --- | --- | | `pending` | 待支付,未收到任何资金 | | `partial_paid` | 部分支付,收到部分资金但不足 | | `paid` | 已支付,收到足够资金 | | `overpaid` | 超额支付,收到的资金超过订单金额 | | `expired` | 已过期 | | `closed` | 已过期且仅收到部分资金 | ##### `pay_status` - 订单处理状态 数据库存储字段,记录订单的处理状态。 | 值 | 说明 | | --- | --- | | `pending` | 待支付 | | `processing` | 处理中(已收到部分资金) | | `paid` | 已支付 | | `partial_paid` | 部分支付已过期 | | `expired` | 已过期未支付 | ##### Webhook 事件 Webhook 中的 `status` 字段和查询接口中的 ` pay_status` 一致。 #### Response 示例 ```json { "order_id": "ord-123", "status": "processing", "pay_status": "processing", "amount": "100", "currency": "USD", "amount_confirming": "0", "amount_confirmed": "0.5", "expires_at": 1763512195, "created_at": 1763512000, "exception_tags": ["wrong_currency"], "client_reference": "ORDER-001" } ``` ### 6.3 重新签发托管收银台 Token(Reissue Checkout Token) **POST** /v1/acquiring/token/reissue 用于重新生成托管收银台 URL,适用于支付页面关闭、Token 过期等场景。 #### Request Body | 字段 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | order_id | string | 是 | 订单唯一 ID | #### Response ```json { "order_id": "ord-123", "checkout_url": "https://checkout.infini.money/pay/xxxx" } ``` ### 6.4 Webhook(订单状态回调) 商户可在后台配置 Webhook 接收地址。订单状态变化时,Infini 会主动推送以下事件: - order.created - order.processing - order.completed - order.expired - order.late_payment #### Webhook Headers | Header | 说明 | | --- | --- | | X-Webhook-Timestamp | Unix 时间戳 | | X-Webhook-Event-Id | 事件唯一 ID | | X-Webhook-Signature | Webhook HMAC 签名 | #### Webhook Payload 示例 ```json { "event": "order.completed", "order_id": "ord-123", "client_reference": "ORDER-001", "amount": "100", "currency": "USD", "status": "paid", "amount_confirmed": "100", "amount_confirming": "0", "created_at": 1763512195, "updated_at": 1763512573, "exception_tags": [] } ``` Webhook 验签方法请参考 **章节 4:授权与安全机制**。 ### 6.5 错误码(Error Codes) 所有错误返回格式: ```json { "code": 40001, "message": "Invalid request", "detail": "expires_at must be greater than current timestamp" } ``` #### 常见错误码 | HTTP | Code | 描述 | | --- | --- | --- | | 400 | 40003 | amount 必须为正数 | | 400 | 40006 | amount 必须大于 0.01 | | 401 | 401 | HMAC 签名无效 | | 404 | 40401 | 订单不存在 | | 409 | 40902 | client_reference 重复 | | 409 | 40906 | 订单已过期 | ### 6.6 Python 示例(托管收银台模式) 以下示例展示完整流程:创建订单 → 跳转收银台 → Webhook → 重新签发 Token。 #### 6.6.1 创建订单 ```python import hmac, hashlib, base64, time from datetime import datetime, timezone import requests key_id = "merchant-001-prod" secret_key = b"your-secret-key" def create_order(amount): method = "POST" path = "/v1/acquiring/order" gmt_time = datetime.now(timezone.utc).strftime('%a, %d %b %Y %H:%M:%S GMT') signing_string = f"{key_id}\n{method} {path}\ndate: {gmt_time}\n" signature = base64.b64encode( hmac.new(secret_key, signing_string.encode(), hashlib.sha256).digest() ).decode() response = requests.post( f"https://openapi.infini.money{path}", json ={ "amount": amount, "currency": "USD", "client_reference": "ORDER-2024-001", "description": "Product purchase", "expires_at": int(time.time()) + 3600 }, headers ={ "Date": gmt_time, "Authorization": f'Signature keyId="{key_id}",algorithm="hmac-sha256",headers="@request-target date",signature="{signature}"', "Content-Type": "application/json" } ) response.raise_for_status() return response.json() ``` #### 6.6.2 前端跳转到收银台 ```python @app.route('/create-payment', methods =['POST']) def create_payment(): order = create_order(amount = request.json['amount']) return {"checkout_url": order["checkout_url"]} ``` #### 6.6.3 Webhook 回调处理 ```python @app.route('/webhook', methods = ['POST']) def handle_webhook(): event = request.json if event['event'] == 'order.completed': process_fulfillment(event['order_id'], event['amount_confirmed']) elif event['event'] == 'order.processing': update_order_progress(event['order_id'], event['status']) elif event['event'] == 'order.expired': mark_order_expired(event['order_id']) return {"status": "ok"} ``` #### 6.6.4 重新签发收银台 Token ```python def reissue_checkout_token(order_id): method = "POST" path = "/v1/acquiring/token/reissue" gmt_time = datetime.now(timezone.utc).strftime('%a, %d %b %Y %H:%M:%S GMT') signing_string = f"{key_id}\n{method} {path}\ndate: {gmt_time}\n" signature = base64.b64encode( hmac.new(secret_key, signing_string.encode(), hashlib.sha256).digest() ).decode() response = requests.post( f"https://api.infini.money{path}", json ={"order_id": order_id}, headers ={ "Date": gmt_time, "Authorization": f'Signature keyId="{key_id}",algorithm="hmac-sha256",headers="@request-target date",signature="{signature}"', "Content-Type": "application/json" } ) response.raise_for_status() return response.json() ```