# 国内认证与支付接入方案 ## 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。 - 阿里云短信 `SendSms` provider:使用租户级 AccessKey、签名、模板发送。 - 腾讯云短信 `SendSms` provider:使用租户级 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.1` fake server。 ### 阿里云短信配置示例 ```json { "provider": "aliyun", "status": "active", "configPublic": { "signName": "工学教育", "templateCode": "SMS_123456789", "regionId": "cn-hangzhou" }, "secret": { "secretScope": "sms", "secretKey": "aliyun", "secretJson": { "accessKeyId": "LTAI...", "accessKeySecret": "..." } } } ``` ### 腾讯云短信配置示例 ```json { "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_miniapp` provider 配置。 - `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。 配置示例: ```json { "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 模块里。 ## 支付 Provider 支付不走 Supabase 内置能力。推荐继续扩展 `commerce`: - `POST /api/commerce/orders` 只负责创建订单,金额以后端套餐为准。 - `POST /api/commerce/payments/create` 按 provider 创建支付参数或收银台地址。 - `POST /api/commerce/payments/notify/` 统一落 `payment_events`,先验签、再幂等、再更新订单和权益。 - 支付成功继续复用 `grantSvipEntitlement`,避免微信/支付宝/XPay 各写一套开通逻辑。 当前支持: - `wechat_pay`:微信支付 API v3 JSAPI 下单;通知验签后用 API v3 key 解密 `resource`。 - `alipay`:支付宝 WAP/H5 支付参数生成;通知按 RSA2 验签。 - `manual`:仅本地/运营手工确认,不作为生产自动支付。 ### 微信支付配置示例 ```json { "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=", "wechatpayPublicKey": "微信支付平台证书公钥或平台公钥" }, "secret": { "secretScope": "payment", "secretKey": "wechat_pay", "secretJson": { "privateKey": "商户 API 证书私钥 PEM", "apiV3Key": "32位 API v3 key" } } } ``` ### 支付宝配置示例 ```json { "provider": "alipay", "mode": "tenant_collect", "status": "active", "configPublic": { "appId": "2021000000000000", "notifyUrl": "https://api.example.com/api/commerce/payments/notify/alipay?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/