---
name: payment-onboarding
owner: 老板（外部资源申请）
version: 2
last-updated: 2026-04-23
---

# SOP: 真支付接入

## ✅ 2026-04-23 现状：支付宝零等待复用

**xiangqin 已切到 alipay provider，应用 + 证书全复用自 `alipay.enterprise.kongxuanpin`**：

- `app_id = "2021000104691762"`（企业应用"阿空选品助手"，已签约当面付）
- `gateway_url = prod openapi`
- `app_private_key / alipay_public_key`（PEM 已在 vault，2026-03-29 实测 prod precreate 成功）

xiangqin 的 alipay provider 启动时直接 `vault get alipay.enterprise.kongxuanpin` 读，**不需要申请任何新应用 / 新证书**。

生产 systemd 环境变量已配 `XIANGQIN_PAYMENT_PROVIDER=alipay` + `XIANGQIN_BASE_URL=http://112.124.27.213`（支付宝 notify_url 走这个）。

**真扣款验收步骤**（老板本人自测）：

```bash
# 1. 本地装最新 xq（走 pypi canary）
pip install --pre acong-tech-xiangqin==0.3.0.dev<ts>   # ts 见 scripts/publish-pypi.sh 输出

# 2. 清一套测试 session
rm -rf /tmp/xq-live

# 3. 注册（真手机号收短信）
XIANGQIN_CONFIG_DIR=/tmp/xq-live xq register 138xxxxxxxx
XIANGQIN_CONFIG_DIR=/tmp/xq-live xq verify <6 位真验证码> --request-id <...>
XIANGQIN_CONFIG_DIR=/tmp/xq-live xq profile set gender m
# ... 等资料填完

# 4. 下单 ¥0.10（买 1 次曝光，真扣款）
XIANGQIN_CONFIG_DIR=/tmp/xq-live xq expose buy --count 1
# terminal 渲染 ASCII 二维码 + 打印 qr URL
# 手机支付宝扫 → 支付 ¥0.10 → 等 webhook 几秒到达

# 5. 余额应该从 0 → 1
XIANGQIN_CONFIG_DIR=/tmp/xq-live xq expose balance
# 剩余曝光次数: 1
```

⚠️ 注意事项:
- 支付宝 notify_url 当前用 **HTTP**（非 HTTPS）。支付宝会显示警告但仍能回调。生产建议 0.4 里程碑上 HTTPS（绑域名 + Let's Encrypt）
- 测试用 ¥0.01 会被支付宝拒（最低 ¥0.10）；用 ¥0.10 真扣款
- xiangqin 的企业支付宝账号收到的款会进对公账户，个人扫码者看到"杭州阿空智能科技有限公司"收款方

---

## 历史路径：全新申请（未来微信 0.4 接入 / 或新应用时走这条）

⚠️ 外部审核。总耗时 **1-2 周**（最慢微信支付开通 + 证书分发那段），老板本人操作。

## 前置

- epsilon 服务端升级到 **HTTPS**（微信支付回调必须 HTTPS；支付宝允许 HTTP 但推荐 HTTPS）：
  - 绑一个域名（例 `api.xiangqin.cc`，cloudflare zone 里加 A 记录指向 112.124.27.213）
  - nginx 加 Let's Encrypt 证书（`certbot --nginx`，epsilon 上跑一次）
  - 阿里云安全组开 443

## 方案 A：微信支付 Native（扫码）

### 第 1 步：开通商户号

https://pay.weixin.qq.com/ → "申请接入"

**材料**：
- 企业营业执照（必须，个人不能开）
- 法人身份证正反
- 对公银行账户
- 店铺 / 业务描述（"相亲付费置顶功能"）

**审核**：5-15 个工作日。通过后拿 **商户号 MCHID**。

### 第 2 步：生成商户证书 + API V3 密钥

微信商户平台登录 → "账户中心" → "API 安全"

- **下载证书工具** 生成 `apiclient_key.pem` + `apiclient_cert.pem`（私钥留在本机）+ `serial_no`
- **设置 APIv3 密钥**（32 位随机字符串，一次性设置，遗失只能重设 + 重新下证书）

### 第 3 步：vault 存凭证

```bash
vault set wechat-pay app_id <AppID>
vault set wechat-pay mch_id <商户号 MCHID>
vault set wechat-pay serial_no <证书序列号>
vault set wechat-pay apiv3_key <32 位 APIv3 密钥>
# 证书 pem 多行文本，建议用文件存储 data/files/
cp apiclient_key.pem /Users/yarnb/vault/data/files/wechat-pay/apiclient_key.pem
cp apiclient_cert.pem /Users/yarnb/vault/data/files/wechat-pay/apiclient_cert.pem
# 更新 data/files/.checksum.json 基线
cd /Users/yarnb/vault && uv run vault verify-files --init --force
```

### 第 4 步：填 src/xiangqin/payment/wechat.py 的 TODO

代码骨架里每个 TODO 都标了要做的 6 步，按 https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/api-v3/open/chapter1_1_1.shtml 实施。

### 第 5 步：webhook 路由 + HTTPS

app.py 加路由 `/exposure/webhook/wechat`，调 `provider.verify_webhook(body, headers)` → 更新订单 + 充值。

### 第 6 步：切 provider

```bash
systemctl edit xiangqin
# 添加:
#   Environment="XIANGQIN_PAYMENT_PROVIDER=wechat"
systemctl daemon-reload && systemctl restart xiangqin
```

---

## 方案 B：支付宝当面付（扫码）

和微信类似。差异：

### 第 1 步：开通应用

https://open.alipay.com/ → "开发者中心" → "创建应用"

**材料**：同微信（企业 + 证件）
**产品**：选"当面付"

### 第 2 步：签约当面付

应用管理 → 申请"当面付"产品。审核 1-3 天。

### 第 3 步：应用私钥 + 支付宝公钥

- 开发者中心"密钥管理" → 上传应用公钥（自己用工具生成的）
- 支付宝回传 alipay_public_key（下载保存）

### 第 4 步：vault 存

```bash
vault set alipay app_id <AppID>
vault set alipay sign_type RSA2
vault set alipay gateway "https://openapi.alipay.com/gateway.do"
# 私钥 / 公钥 pem 放 data/files/alipay/
```

### 第 5 步：填 src/xiangqin/payment/alipay.py 的 TODO

参考 https://opendocs.alipay.com/open/194/105170

### 第 6 步：切 provider

```bash
Environment="XIANGQIN_PAYMENT_PROVIDER=alipay"
```

---

## 双支付共存（高阶）

用环境变量只能选一个。真正做双支付是业务 / 产品问题：
- 前端 / CLI 让用户选"用微信 / 支付宝"
- 每次 `expose buy --method wechat|alipay`
- 服务端同时加载两个 provider 实例按参数路由

这是 0.3 才考虑的事。0.2 先让一家跑通。

## 坑

- 未备案域名发布支付回调会被封。域名 ICP 备案 3-20 天（阿里云买的域名自带快速备案）
- 微信支付 APIv3 的签名算法比短信复杂得多（RSA-SHA256 + AES-GCM 解密），一定要用官方文档对拍
- 支付宝的 sign_type 要用 RSA2（不是老版 RSA）
- 订单号 `out_trade_no` 必须幂等（同一个 order_id 重发多次应拿到同一支付二维码）
- webhook 必须返回特定字符串（微信 `{"code":"SUCCESS","message":"成功"}` / 支付宝 `success`），否则会一直重试
