18925d7d15
- POST /license/purchase(仅管理员)建单并调 pay 下单,返回收银台 pay_url - POST /pay/callback 公开接收器:HMAC 验签+时间戳窗口+按 out_trade_no 幂等+金额逐分核对,同事务续期 - 续期与兑换券同口径:未过期从到期日叠加、已过期从现在起算,写入 tier/max_devices/features - 后台每 60s 查单兜底防 webhook 丢失;closed 标 failed - 新表 license_purchases(schema.sql/AutoMigrate/testutil 同步);配置 PAY_SECRET/PAY_BASE_URL/PAY_RETURN_URL - 契约真相源 ~/code/pay-contract openapi.yaml v1.0.0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
22 lines
693 B
Go
22 lines
693 B
Go
package util
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"strings"
|
|
)
|
|
|
|
// PaySign pay 收款中枢双向 HMAC 签名(契约 ~/code/pay-contract §签名,与 pay 侧 util.HMACSign 一致):
|
|
// sign = base64( HMAC_SHA256( secret, biz_system + "\n" + timestamp + "\n" + nonce + "\n" + rawBody ) )
|
|
func PaySign(secret string, parts ...string) string {
|
|
m := hmac.New(sha256.New, []byte(secret))
|
|
m.Write([]byte(strings.Join(parts, "\n")))
|
|
return base64.StdEncoding.EncodeToString(m.Sum(nil))
|
|
}
|
|
|
|
// PaySignVerify 常量时间比较验签。
|
|
func PaySignVerify(secret, got string, parts ...string) bool {
|
|
return hmac.Equal([]byte(PaySign(secret, parts...)), []byte(got))
|
|
}
|