首次调用 API
SpacePay 商户 API 用来创建支付、查询订单、发起退款和管理结算。接口使用 HTTPS + JSON,金额以整数分存储,同时返回小数金额。
!本环境的余额为虚拟资金,不接入真实银行卡或支付渠道,结算出款为模拟流程。接口语义、签名与幂等行为与生产实现一致,可用于完整联调。
接入流程
入驻商户
登录 SpacePay,在「我的 → 商户后台」提交入驻申请,等待运营审核。
创建 API Key
审核通过后,在商户后台「API 密钥」创建 sp_live_…。密钥只显示一次,服务端只保存哈希。
创建支付
调用 POST /api/v1/payments,把返回的 checkout_url 交给付款人。
处理 Webhook
付款完成后收到 payment.succeeded,校验签名并更新订单状态。
接口地址与鉴权
| 项目 | 值 |
|---|---|
| 接口地址 | https://your-domain/api/v1当前部署: |
| 鉴权方式 | Authorization: Bearer sp_live_<key_id>_<secret>,或请求头 X-API-Key |
| 数据格式 | Content-Type: application/json,UTF-8 |
| 金额 | 入参 amount 为十进制字符串(如 "12.34");响应同时返回 amount_cents(整数分) |
| 时间 | 毫秒时间戳(UNIX epoch, ms) |
| 币种 | 当前仅支持 USD |
i密钥是服务端凭据,务必放在服务端环境变量中,不要写进浏览器、移动端包体或 Git 仓库。怀疑泄露时在商户后台立即吊销。
发起第一笔支付
把 SPACEPAY_KEY 设置为你的密钥,然后发送请求:
export SPACEPAY_KEY="sp_live_xxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyy"
curl "https://your-domain/api/v1/payments" \
-X POST \
-H "Authorization: Bearer $SPACEPAY_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"amount": "12.34",
"reference": "order-2026-0001",
"description": "拿铁两杯",
"customer_email": "[email protected]",
"metadata": { "sku": "latte" },
"success_url": "https://shop.example/paid"
}'
# pip install requests
import os
import uuid
import requests
resp = requests.post(
"https://your-domain/api/v1/payments",
headers={
"Authorization": f"Bearer {os.environ['SPACEPAY_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"amount": "12.34",
"reference": "order-2026-0001",
"description": "拿铁两杯",
"customer_email": "[email protected]",
"metadata": {"sku": "latte"},
"success_url": "https://shop.example/paid",
},
timeout=15,
)
resp.raise_for_status()
payment = resp.json()["payment"]
print(payment["checkout_url"])
// Node.js 18+(内置 fetch)
import { randomUUID } from 'node:crypto';
const resp = await fetch('https://your-domain/api/v1/payments', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SPACEPAY_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': randomUUID(),
},
body: JSON.stringify({
amount: '12.34',
reference: 'order-2026-0001',
description: '拿铁两杯',
customer_email: '[email protected]',
metadata: { sku: 'latte' },
success_url: 'https://shop.example/paid',
}),
});
const { payment } = await resp.json();
console.log(payment.id, payment.checkout_url);
返回示例:
{
"ok": true,
"payment": {
"id": "pay_1",
"numeric_id": 1,
"reference": "order-2026-0001",
"amount_cents": 1234,
"amount": "12.34",
"currency": "USD",
"status": "CREATED",
"refunded_cents": 0,
"refundable_cents": 1234,
"description": "拿铁两杯",
"customer_email": "[email protected]",
"metadata": { "sku": "latte" },
"checkout_url": "https://your-domain/pay.html?token=…",
"created_at": 1762800000000,
"expires_at": 1762801800000
}
}
让付款人完成支付
把 checkout_url 交给付款人的浏览器。付款人登录 SpacePay、输入 6 位支付 PIN 后完成付款;支付单在 30 分钟内有效。
不要只依赖付款人跳回 success_url 判断支付成功——跳转可能被中断。以 Webhook 为准,必要时再调用 查询支付 确认。
验证 Webhook
付款成功后,SpacePay 会向商户后台配置的 HTTPS 地址推送 payment.succeeded。请求头 X-SpacePay-Signature 形如 t=时间戳,v1=签名,签名是 HMAC-SHA256(webhook_secret, 时间戳 + "." + 原始请求体)。
import crypto from 'node:crypto';
function verify(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(',').map((p) => p.split('=')));
const expected = crypto.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
import hashlib
import hmac
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in signature_header.split(","))
expected = hmac.new(
secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
完整的事件与重试规则见 Webhook 与签名。