流程总览
- 用户在
/license/buy填写域名并选择支付渠道。 - 服务端解析域名与回跳地址,创建(或复用)待授权记录与订单,向支付渠道下单,得到二维码内容。
- 跳转到
/pay/{out}展示二维码,前端轮询/pay/status/{out}。 - 支付成功后(回调或主动查询),订单置为已支付并签发授权令牌。
- 轮询返回
paid及跳转地址,跳转到/pay/done/{out}或回跳站点并附带令牌。
GET /license/buy
查询参数:
| 参数 | 说明 |
|---|---|
domain | 要授权的域名(可含 www.、协议、路径,服务端会规范化)。 |
return | 可选。支付完成后回跳的地址。 |
域名解析顺序
- 优先使用
domain查询参数。 - 若
domain为空,则回退到Referer请求头的主机名。
Referer 处理规则:仅接受 http 或 https 协议的 Referer;主机名规范化(小写、去端口、IDN→punycode)后,若为 kingcms.com 或 download.kingcms.com 则忽略(视为空)。
无论来源如何,主机名都会先规范化(去 www.、小写、punycode),再计算根域名(基于 Public Suffix List,支持 com.cn 等多段后缀,例如 shop.example.com.cn → example.com.cn)。页面展示与授权均以根域名为准。
当域名来自 Referer 且该主机名与根域名不同(即访问的是子域名,如 shop.example.com),页面会显示「来自 <host>」提示,说明授权将覆盖整个根域名及其全部子域名。
return 校验规则
必须是 http 或 https,且其主机名(规范化后)必须等于所授权的主机名(即 domain 参数规范化后的完整主机名,而非根域名)。不满足则忽略该参数。
当域名由 Referer 推导而来(domain 参数为空)时,若未显式提供合法的 return,则默认使用 Referer 本身作为回跳地址(同样需通过上述主机名匹配校验)。
已授权行为
页面会显示价格、可用渠道。若该根域名已有有效授权(active)或已签发有效令牌,页面会一并展示该令牌,并标记 HasLicense / HasToken,提示用户无需重复购买。
价格展示
- 原价(list price):配置的标价。
- 优惠价(promo price):配置的促销价,受「促销开关」控制。
- 生效价:促销开关开启且促销价 > 0 时取促销价,否则取原价。
所有价格均以「分」为单位在服务端计算与校验,前端展示的金额仅供参考,下单金额一律以服务端计算为准。
POST /license/buy
表单字段:
| 字段 | 必填 | 说明 |
|---|---|---|
domain | 是 | 要授权的域名,服务端规范化后取根域名。 |
return | 否 | 回跳地址,需通过主机名匹配校验。 |
channel | 是 | alipay 或 wechat。 |
coupon | 否 | 优惠码(服务端会转大写并去除首尾空白)。 |
buyer_name | 否 | 购买人姓名。 |
buyer_company | 否 | 公司名称。 |
buyer_email | 否 | 联系邮箱。 |
buyer_phone | 否 | 联系电话。 |
需通过公开 CSRF 校验。
校验与处理
- 域名规范化后计算根域名(基于 Public Suffix List,支持
com.cn等多段后缀)。 - 价格未配置(≤0)或渠道未启用时返回错误提示。
- 若填写了优惠码:服务端校验优惠码存在、状态为未使用、批次未过期,并按优惠类型计算最终金额(见下)。
- 按根域名查找或创建待授权记录,生成商户订单号(
KC+ 北京时间yyyymmddhhmmss+ 6 位随机数字)。 - 调用渠道下单接口获取二维码内容,写入订单,
303跳转到/pay/{out}。 - 若使用了优惠码,订单创建后立即占用该优惠码(状态置为
reserved并绑定订单与域名);占用失败则关闭订单并提示优惠码已被使用。 - 可选购买人字段在授权记录存在对应列时写入。
优惠码与最终价格
优惠码在服务端解析,客户端提交的价格一律不信任。优惠类型:
- 券后价(
final):最终金额直接等于优惠码设定的金额。 - 立减(
off):最终金额 = 生效价 − 优惠金额。
最终金额最低为 ¥0.01(1 分),低于此值按 1 分计。
优惠码为一次性:下单时占用(reserved),支付成功时消费(used);若订单在 30 分钟内未支付,占用会被释放回未使用状态,可再次使用。
POST /license/coupon
公开的优惠码试算接口,用于在购买页实时显示折后价。需通过公开 CSRF 校验,并受每 IP 限流(超出返回 429)。
表单字段:
| 字段 | 说明 |
|---|---|
code | 优惠码(服务端转大写并去除首尾空白)。 |
domain | 可选,用于上下文展示。 |
csrf | 公开 CSRF 令牌。 |
返回 JSON:
{
"ok": true,
"list": "2999.00",
"promo": "299.00",
"final": "199.00",
"message": "优惠码已应用"
}
ok:优惠码是否可用。list:原价(元)。promo:生效价(元,促销开关开启时为促销价,否则为原价)。final:应用优惠码后的最终价(元);不可用时等于promo。message:提示文案(已应用 / 无效 / 已使用 / 已过期 / 限流等)。
该接口仅用于展示,实际下单金额仍以 POST /license/buy 服务端计算为准。
GET /pay/{out}
展示订单金额、渠道、二维码(/pay/qr/{out}.png)、轮询地址(/pay/status/{out})与完成地址(/pay/done/{out})。若订单已支付则直接跳转到完成页。
GET /pay/qr/{file}
返回订单二维码 PNG(256px,中等纠错)。文件名形如 {out}.png。
GET /pay/status/{out}
返回 JSON:
- 待支付:
{"status":"pending"} - 已支付:
{"status":"paid","redirect":"<url>"}。若订单有合法回跳地址且已签发令牌,redirect为回跳地址并附带token查询参数;否则为/pay/done/{out}。 - 不存在:
404,{"status":"not_found"}
对处于待支付且创建超过 5 秒的订单,服务端会(同一订单每 5 秒最多一次)主动向渠道查询并结算。
GET /pay/done/{out}
展示完成页:订单、令牌、域名、回跳地址,以及「回跳地址 + token」的完整链接(若两者都存在)。页面同时提供授权证书查询链接(/license/check?domain=<domain>),方便用户核对授权状态。
支付回调
POST /pay/notify/alipay
- 校验支付宝异步通知签名(使用配置的支付宝公钥)、
app_id一致。 - 仅处理
trade_status为TRADE_SUCCESS/TRADE_FINISHED;其他状态直接应答success不处理。 - 校验金额与订单一致。
- 成功应答纯文本
success。
POST /pay/notify/wechat
- 校验微信支付回调签名(
Wechatpay-Timestamp/Wechatpay-Nonce/Wechatpay-Signature),时间戳偏差超过 5 分钟拒绝。 - 使用 APIv3 密钥解密
AEAD_AES_256_GCM资源,校验trade_state为SUCCESS、金额一致。 - 成功应答
{"code":"SUCCESS"}。
幂等性
结算通过「将订单从待支付原子地置为已支付」实现:只有真正发生状态迁移的那一次才会签发授权与记录收款,重复回调或重复查询不会重复签发。优惠码的消费同样只在状态迁移成功时发生一次。