让 jiu(岩美酒库)门店在应用内购买/续费授权:付款走 pay 收款中枢,付成功后 pay 回调 jiu,jiu 直接给该门店续期。
pay 已是独立收款中枢(支付宝电脑网站支付 page.pay + 手机网站支付 wap.pay UA 自适应,已上线验证到账)。jiu 已有完整授权体系(License / LicenseCode / 兑换续期)。目标:把两者接起来——jiu 门店在应用内选套餐付款,付成功后无感自动续期,不再靠线下发兑换码。
| 方法/路径 | 说明 |
|---|---|
POST /api/v1/orders | 下单,返回 pay_url(UA 自适应 PC 扫码 / 手机拉 App)。当前只收 product_id,无业务标识、无鉴权 |
GET /api/v1/orders/:out_trade_no | 查订单状态 |
POST /api/v1/notify/{alipay,wechat} | 支付宝/微信异步回调(入账,已实现) |
| 模型 | 关键字段 | 语义 |
|---|---|---|
License | ShopID · Type(trial/monthly/annual/lifetime) · Tier · ExpiresAt · MaxDevices · IsActive | 门店当前授权状态 |
LicenseCode | Code · DurationDays · Tier · Type · Status(unused/redeemed/void) · RedeemedShopID | 兑换券:兑换时把 DurationDays 叠加到门店到期时间;一码一次 |
现有续期路径:平台生成码 → 用户获得 → 门店兑换。「购买」这步目前是线下/手动——本方案就是把它自动化。
为什么是 B:购买发生在已登录门店,jiu 天然知道是哪个 ShopID,付成功直接续期,用户无感,体验最好。
| 字段 | 类型 | 说明 |
|---|---|---|
BizSystem | string(index) | 业务来源,如 jiu;决定回调哪个 webhook / 用哪个密钥 |
BizRef | string | 业务引用,jiu 传(如 shop_id 或 jiu purchase_id);webhook 原样回传 |
BizReturnURL | string(可选) | 付完跳回业务自己的页面;空则用 pay 默认结果页 |
BizCode(如 annual_standard):webhook 回传给 jiu,jiu 按稳定码而非数字 product_id 映射权益,避免硬编码耦合。价格权威仍在 pay 套餐表。
记录每次向业务系统回调的 URL / payload / 响应 / 重试次数,用于排障与重放。
| 字段 | 说明 |
|---|---|
| ShopID · ProductBizCode · Amount | 谁买、买什么、多少钱 |
| OutTradeNo | pay 订单号(对账 + 幂等键) |
| Status(pending/paid/failed) · PaidAt | 状态机 |
POST /api/v1/orders请求头带签名(见 §6)。Body 扩展:
{
"product_id": 3,
"biz_system": "jiu",
"biz_ref": "shop_1024",
"return_url": "https://jiu.51yanmei.com/license/result" // 可选
}
Headers:
X-Pay-System: jiu
X-Pay-Timestamp: 1751520000
X-Pay-Nonce: 8f3a...
X-Pay-Sign: base64(HMAC-SHA256(secret, system+timestamp+nonce+body))
pay 校验签名 → 按 product_id 取服务端权威价格建单、存 biz 字段 → 返回 pay_url。
pay 在 applyPaid 成功后,按 order 的 biz_system 找到回调配置(URL+secret),POST:
POST {jiu 配置的回调 URL}
Headers: X-Pay-Timestamp, X-Pay-Nonce, X-Pay-Sign (HMAC-SHA256)
Body:
{
"out_trade_no": "yanmei-20260703...-xxxx",
"biz_system": "jiu",
"biz_ref": "shop_1024",
"product_biz_code": "annual_standard",
"amount": "268.00",
"trade_no": "20260703...", // 支付宝/微信交易号
"channel": "alipay",
"paid_at": "2026-07-03T14:36:30+08:00"
}
期望响应:HTTP 200 + {"code":"SUCCESS"};否则 pay 按退避重试
POST /api/v1/license/purchase鉴权=jiu 自己的登录态(当前用户/门店)。选套餐 → 建 Purchase(pending) → 调 pay §5.1 下单 → 返回 pay_url 给客户端。
POST /api/v1/pay/callbackout_trade_no 查 Purchase,已处理则直接回 SUCCESSproduct_biz_code → (duration_days, tier, type)License.ExpiresAt){"code":"SUCCESS"};标记 Purchase=paidpay-jiu webhook secret),部署经 rbw 灌环境变量,不落配置/仓库。system+timestamp+nonce+body,防篡改。out_trade_no 唯一处理——pay 可能重发 webhook、jiu 只续一次。pay 套餐表持有价格(99/268/888/2280),每个套餐挂稳定 biz_code;jiu 侧一张映射表把 biz_code 翻成时长/档位:
| pay product | biz_code | 价格 | jiu 续期(直接叠加 ExpiresAt) |
|---|---|---|---|
| 月付·标准 | monthly_standard | ¥299 | +30 天, tier=standard, type=monthly |
| 月付·高级 | monthly_pro | ¥599 | +30 天, tier=pro, type=monthly |
| 年付·标准 | annual_standard | ¥2999 | +365 天, tier=standard, type=annual |
| 年付·高级 | annual_pro | ¥5999 | +365 天, tier=pro, type=annual |
权益差异(2026-07-03 已定,都是单店):
| 档 | 客户端 | 仓库 | 图片分享 | AI 商业分析 |
|---|---|---|---|---|
| 标准(299/2999) | 2 | 单仓库 | 千张 | — |
| 高级(599/5999) | 5 | 多仓库 | 万张 | 免费 AI 周/月度商业数据分析 |
pay 侧只透传 biz_code,不关心权益细节;权益→License(tier/max_devices/features) 的落地在 jiu 侧,见 jiu 仓 docs/pay支付对接开发指南.html。
GET /orders/:out_trade_no 补偿(防 webhook 全丢)。阶段 1 · pay 侧:Order 加 biz 字段 + Product 加 biz_code;下单接口收 biz + 签名鉴权;BizNotifyLog。
阶段 2 · pay 侧:入账后业务 webhook 推送 + 退避重试;回调配置(每个 biz_system 的 URL+secret)。
阶段 3 · jiu 侧:Purchase 模型 + 购买接口(调 pay 下单)。
阶段 4 · jiu 侧:webhook 接收器(验签+幂等+映射+续期,复用 license 服务)+ 查单兜底。
阶段 5:jiu 客户端购买/续费 UI;真实 1 分钱端到端联调;对账脚本。
| # | 问题 | 决策 |
|---|---|---|
| 1 | 套餐定义 | 月 ¥299/¥599、年 ¥2999/¥5999(2 档 × 月/年,见 §7)。标准/高级权益差异待定 |
| 2 | 续期实现 | 直接叠加 License.ExpiresAt(不走生成兑换码) |
| 3 | biz_ref | jiu 的 purchase_id(利对账/多态) |
| 4 | 付款后 | 跳回 jiu 结果页(return_url) |
| 5 | 客户端形态 | Web 入口 + App 入口都要;App 用内置 webview 打开 pay_url。付完 return_url 跳回;webview 内拉起支付宝 App 需处理 scheme 唤起/回跳 |
alipays:// 唤起 App,付完靠 return_url 回跳 jiu 页面(比 PC 多一层客户端处理,Phase 5 联调)。