10 KiB
国内认证与支付接入方案
Supabase 边界
Supabase 适合承担 PostgreSQL、RLS、Auth、Edge Functions、Webhook/Hooks 等底座能力,但它不是中国大陆支付网关,也不会内置微信支付、支付宝、阿里云短信、腾讯云短信这一整套商用配置。
对本项目更稳妥的落位是:
- Supabase/PostgreSQL:保存多租户、订单、支付事件、权益、审计、登录事件。
apps/api:实现业务 API、短信 provider、OAuth provider、支付 provider、回调验签和幂等。app_private.tenant_secrets或生产 Vault/KMS:保存租户级密钥。tenant_auth_providers、tenant_payment_accounts:只保存非敏感公开配置。
Supabase Auth 可继续作为最终 JWT 用户体系目标;本地重构期先用 app_private.auth_sessions 签发 tk_ session,保证 Web/Taro 能跑通端到端流程。
当前已实现
POST /api/auth/sms/send:手机号验证码发送,验证码只保存 HMAC hash。POST /api/auth/sms/verify:验证码登录,自动创建或复用platform_users。GET /api/auth/me:通过 Bearer token 获取当前用户。POST /api/auth/logout:吊销迁移期 session。POST /api/auth/oauth/wechat-miniapp:微信小程序code2Session登录,后端换取 openid/unionid,签发tk_session。POST /api/auth/oauth/wechat、/qq:provider 占位,已固定PROVIDER_NOT_CONFIGURED/PROVIDER_NOT_IMPLEMENTED。tenant_auth_providers:租户级公开认证配置。sms_verification_codes:验证码审计表,不保存明文 code。auth_login_events:登录事件审计。app_private.auth_sessions:迁移期 session token hash。- 阿里云短信
SendSmsprovider:使用租户级 AccessKey、签名、模板发送。 - 腾讯云短信
SendSmsprovider:使用租户级 SecretId/SecretKey、SdkAppId、签名、模板发送。 POST /api/commerce/payments/create:按订单创建微信支付 JSAPI 或支付宝 WAP 支付参数。POST /api/commerce/payments/notify/wechat_pay:微信支付 API v3 通知验签、AES-GCM 解密、幂等落库和权益开通。POST /api/commerce/payments/notify/alipay:支付宝 RSA2 通知验签、幂等落库和权益开通。
短信 Provider
本地默认是 AUTH_SMS_PROVIDER=mock,仅开发环境返回 debugCode。生产环境如果仍为 mock,会直接拒绝发送。
真实 provider:
aliyun/aliyun-sms:阿里云短信SendSms,需要 AccessKey、签名、模板 ID。tencent/tencent-sms:腾讯云短信SendSms,需要 SecretId、SecretKey、SdkAppId、签名、模板 ID。
密钥策略:
- AccessKey/SecretKey 不进入
tenant_settings.public_config。 - 租户级密钥写
app_private.tenant_secrets(secret_scope='sms')或生产 Vault。 - 前端只能看到 provider 是否启用、签名展示名、隐私协议链接等非敏感配置。
- provider endpoint 默认使用官方域名,生产环境只允许 HTTPS 官方域名;本地测试可使用
localhost/127.0.0.1fake server。
阿里云短信配置示例
{
"provider": "aliyun",
"status": "active",
"configPublic": {
"signName": "工学教育",
"templateCode": "SMS_123456789",
"regionId": "cn-hangzhou"
},
"secret": {
"secretScope": "sms",
"secretKey": "aliyun",
"secretJson": {
"accessKeyId": "LTAI...",
"accessKeySecret": "..."
}
}
}
腾讯云短信配置示例
{
"provider": "tencent",
"status": "active",
"configPublic": {
"smsSdkAppId": "1400000000",
"signName": "工学教育",
"templateId": "123456",
"region": "ap-guangzhou",
"templateParamSet": ["{code}"]
},
"secret": {
"secretScope": "sms",
"secretKey": "tencent",
"secretJson": {
"secretId": "AKID...",
"secretKey": "..."
}
}
}
微信/QQ 登录
微信小程序登录应由前端传 wx.login code 到 /api/auth/oauth/wechat-miniapp,后端调用微信 code2Session 换取 openid/session_key/unionid,再落 user_identities。
已实现微信小程序登录主链路:
- 前端传
code和可选profile。 - 后端读取租户
wechat-miniapp/wechat_miniappprovider 配置。 appId存在config_public,appSecret存在app_private.tenant_secrets(secret_scope='oauth')。provider_subject使用appId:openid,避免不同小程序 openid 碰撞。session_key不返回前端,不写入公开raw_profile;当前只记录hasSessionKey和更新时间标记。- 登录成功写
auth_login_events,并签发tk_session。
配置示例:
{
"provider": "wechat-miniapp",
"displayName": "微信小程序登录",
"status": "active",
"configPublic": {
"appId": "wx...",
"envVersion": "release"
},
"secret": {
"secretScope": "oauth",
"secretKey": "wechat-miniapp",
"secretValue": "小程序 AppSecret"
}
}
微信网页 OAuth 和 QQ OAuth 也必须在后端完成 code 换 token、获取 openid/unionid、验错、账号合并和登录事件审计。旧 PocketBase hooks 中的邀请码/销售归属逻辑后续应拆到 referral feature,不继续堆在 auth 模块里。
手机号绑定/换绑
已实现 POST /api/auth/phone/bind。适用场景包括微信/QQ 登录后强制绑定手机号,以及个人中心更换手机号。
流程:
- 前端调用
POST /api/auth/sms/send,purpose必须是bind_phone。 - 前端在登录态下调用
POST /api/auth/phone/bind,提交phone和验证码。 - 后端校验当前 session/JWT 属于当前租户,不信任
x-user-id。 - 后端校验手机号未被其它账号占用,成功后更新
platform_users.phone和user_identities(provider='phone')。 - 换绑成功会删除当前用户旧手机号 identity,并撤销其它迁移期
tk_session;当前 session 保持可用。
前端不能用 login 用途验证码绑定手机号;接口会返回 PHONE_BIND_PURPOSE_REQUIRED。手机号已被其它账号占用时返回 PHONE_ALREADY_BOUND。
支付 Provider
支付不走 Supabase 内置能力。推荐继续扩展 commerce:
POST /api/commerce/orders只负责创建订单,金额以后端套餐为准。POST /api/commerce/payments/create按 provider 创建支付参数或收银台地址。POST /api/commerce/payments/notify/<provider>统一落payment_events,先验签、再幂等、再更新订单和权益。- 支付成功继续复用
grantSvipEntitlement,避免微信/支付宝/XPay 各写一套开通逻辑。
当前支持:
wechat_pay:微信支付 API v3 JSAPI 下单;通知验签后用 API v3 key 解密resource。alipay:支付宝 WAP/H5 支付参数生成;通知按 RSA2 验签。manual:仅本地/运营手工确认,不作为生产自动支付。
微信支付配置示例
{
"provider": "wechat_pay",
"mode": "tenant_collect",
"status": "active",
"configPublic": {
"appId": "wx...",
"merchantId": "1900000001",
"merchantSerialNo": "商户证书序列号",
"notifyUrl": "https://api.example.com/api/commerce/payments/notify/wechat_pay?tenantId=<tenantId>",
"wechatpayPublicKey": "微信支付平台证书公钥或平台公钥"
},
"secret": {
"secretScope": "payment",
"secretKey": "wechat_pay",
"secretJson": {
"privateKey": "商户 API 证书私钥 PEM",
"apiV3Key": "32位 API v3 key"
}
}
}
支付宝配置示例
{
"provider": "alipay",
"mode": "tenant_collect",
"status": "active",
"configPublic": {
"appId": "2021000000000000",
"notifyUrl": "https://api.example.com/api/commerce/payments/notify/alipay?tenantId=<tenantId>",
"returnUrl": "https://h5.example.com/pay/success"
},
"secret": {
"secretScope": "payment",
"secretKey": "alipay",
"secretJson": {
"privateKey": "应用私钥 PEM",
"alipayPublicKey": "支付宝公钥 PEM"
}
}
}
支付回调处理规则:
payment_events(provider,event_id)幂等。- 通知验签/解密失败直接拒绝,不更新订单。
- 通知金额必须等于后端订单金额。
- 订单已支付时重复通知只返回幂等成功,不重复开通权益。
- 支付成功事务内更新
orders、payments、payment_events、entitlements。 - 支付密钥只允许放在
app_private.tenant_secrets或生产 KMS/Vault。
B 端合作商年费、服务费、服务器资源费不走学生端 orders,而是走平台账务:
platform_saas_plans:平台售卖给合作商的 SaaS 套餐。tenant_subscriptions:合作商当前订阅状态。tenant_invoices、tenant_invoice_items:合作商账单与明细。tenant_invoice_payments:合作商账单收款记录。tenant_usage_records:学生数、题量、存储等用量指标。
支持策略:
- 平台代收:平台商户号收款,再给租户结算。
- 租户自收:每个租户配置自己的商户号和密钥。
- 服务商模式:平台服务商统一管理子商户。
真实接入前需要先明确微信支付、支付宝或聚合支付是否允许你们销售的题库会员形态,以及小程序端是否涉及虚拟支付限制。
需要准备的资料
- 阿里云或腾讯云短信:签名、模板 ID、AccessKey/SecretKey、短信用途文案。
- 微信小程序:AppID、AppSecret、主体信息、合法域名、用户手机号授权能力。
- 微信网页/公众号:AppID、AppSecret、授权回调域名。
- QQ 互联:AppID、AppKey、回调域名。
- 微信支付:商户号、API v3 key、商户证书/平台证书、回调域名、AppID 绑定关系。
- 支付宝:AppID、应用私钥、支付宝公钥、回调地址、网页/手机网站/当面付产品开通情况。
- 每个合作商租户的收款模式:平台代收、租户自收或服务商子商户。
参考资料
- Supabase Phone Login: https://supabase.com/docs/guides/auth/phone-login
- Supabase Auth Hooks: https://supabase.com/docs/guides/auth/auth-hooks
- Supabase Social Login: https://supabase.com/docs/guides/auth/social-login
- 阿里云短信 SendSms: https://help.aliyun.com/zh/sms/developer-reference/api-dysmsapi-2017-05-25-sendsms
- 腾讯云短信 SendSms: https://cloud.tencent.com/document/api/382/55981
- 微信小程序登录 code2Session: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html
- QQ 互联 OAuth: https://wiki.connect.qq.com/oauth2-0简介
- 微信支付 API v3: https://pay.weixin.qq.com/doc/v3/merchant/4012791855
- 支付宝开放平台: https://opendocs.alipay.com/