From d1a7ba8119dc3a1658df612778ac03f7d2f5adfd Mon Sep 17 00:00:00 2001 From: ayflying Date: Tue, 29 Jul 2025 10:15:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=8D=A3=E8=80=80=E6=94=AF?= =?UTF-8?q?=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package/pay/honor/const.go | 5 +++ package/pay/honor/honor.go | 60 +++++++++++++++++++++++++++++++ package/pay/honor/model.go | 45 +++++++++++++++++++++++ package/pay/honor/notification.go | 26 ++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 package/pay/honor/const.go create mode 100644 package/pay/honor/honor.go create mode 100644 package/pay/honor/model.go create mode 100644 package/pay/honor/notification.go diff --git a/package/pay/honor/const.go b/package/pay/honor/const.go new file mode 100644 index 0000000..e7c68d1 --- /dev/null +++ b/package/pay/honor/const.go @@ -0,0 +1,5 @@ +package honor + +const ( + Host = "https://iap-api.cloud.honor.com" +) diff --git a/package/pay/honor/honor.go b/package/pay/honor/honor.go new file mode 100644 index 0000000..0dddac9 --- /dev/null +++ b/package/pay/honor/honor.go @@ -0,0 +1,60 @@ +package honor + +import ( + "crypto" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" +) + +type Pay struct { + PubKey string `json:"pubKey"` + AppId string `json:"appId"` +} + +func New() *Pay { + return &Pay{} +} + +// VerifyRSASignature 验证RSA数字签名 +// data: 原始数据字节 +// sign: 签名的Base64编码字符串 +// pubKey: PEM格式的公钥字符串 +// 返回验证结果和可能的错误 +func (p *Pay) VerifyRSASignature(data []byte, sign string) (bool, error) { + // 解码Base64格式的签名 + signBytes, err := base64.StdEncoding.DecodeString(sign) + if err != nil { + return false, errors.New("签名解码失败: " + err.Error()) + } + + // 解析PEM格式的公钥 + block, _ := pem.Decode([]byte(p.PubKey)) + if block == nil { + return false, errors.New("无效的PEM格式公钥") + } + + // 解析公钥 + publicKey, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return false, errors.New("公钥解析失败: " + err.Error()) + } + + // 类型断言为公钥 + rsaPubKey, ok := publicKey.(*rsa.PublicKey) + if !ok { + return false, errors.New("不是有效的RSA公钥") + } + + // 计算数据的SHA-256哈希 + hasher := sha256.New() + hasher.Write(data) + hash := hasher.Sum(nil) + + // 验证签名 + err = rsa.VerifyPKCS1v15(rsaPubKey, crypto.SHA256, hash, signBytes) + return err == nil, err +} diff --git a/package/pay/honor/model.go b/package/pay/honor/model.go new file mode 100644 index 0000000..56fa905 --- /dev/null +++ b/package/pay/honor/model.go @@ -0,0 +1,45 @@ +package honor + +type PayCallbackHeader struct { + Charset string `json:"charset" dc:"字符集,当前只支持utf-8。"` + SignType string `json:"signType" dc:"签名算法类型, 当前只支持RSA"` + Sign string `json:"sign" dc:"notificationMessage的签名,已废弃,请用signature。"` + Signature string `json:"signature" dc:"对data的签名。"` + AppId string `json:"appId" dc:"应用ID"` +} + +type PayCallback struct { + Env string `json:"env" dc:"发送通知的环境,sandbox为沙盒测试环境,非sandbox为正式环境"` + EventType string `json:"eventType" dc:"事件类型,如付款成功、退款失败等"` + EventCode int `json:"eventCode" dc:"事件类型对应的code值"` + Version string `json:"version" dc:"iap版本"` + EventTime string `json:"eventTime" dc:"通知时间"` + Data PayCallbackData `json:"data" dc:"通知内容notificationMessage的json字符串"` +} + +type PayCallbackData struct { + AppId string `json:"appId" dc:"应用ID"` + OrderId string `json:"orderId" dc:"订单ID"` + BizOrderNo string `json:"bizOrderNo,omitempty" dc:"max-length:64#业务订单号"` + ProductType int `json:"productType" dc:"商品类型0:消耗型,1:非消耗型,2:订阅型"` + ProductId string `json:"productId" dc:"商品ID"` + ProductName string `json:"productName" dc:"商品名称"` + PurchaseTime int64 `json:"purchaseTime" dc:"购买时间UTC时间戳(毫秒)"` + PurchaseState int `json:"purchaseState" dc:"订单状态 0:已购买 1:已退款 2:付款失败 3:退款失败 4:未支付 5:退款中"` + ConsumptionState int `json:"consumptionState" dc:"消耗状态 0:未消耗 1:已消耗"` + PurchaseToken string `json:"purchaseToken" dc:"购买令牌"` + Currency string `json:"currency" dc:"币种"` + Price string `json:"price" dc:"商品价格"` + PayMoney string `json:"payMoney" dc:"实际支付金额"` + DeveloperPayload string `json:"developerPayload,omitempty" dc:"max-length:1024#商户信息"` + OriOrder string `json:"oriOrder" dc:"原订单信息"` + SandboxFlag int `json:"sandboxFlag" dc:"沙盒标识"` + AgreementNo string `json:"agreementNo,omitempty" dc:"订阅合约号"` + ExecuteTime string `json:"executeTime,omitempty" dc:"下次扣费时间(订阅)"` + SecondChargeTime int64 `json:"secondChargeTime,omitempty" dc:"第二次扣费时间(订阅升级)"` + OldProductId string `json:"oldProductId,omitempty" dc:"老商品ID(订阅升级)"` + SubStartTime string `json:"subStartTime,omitempty" dc:"订阅开始时间"` + SubEndTime string `json:"subEndTime,omitempty" dc:"订阅结束时间"` + OriginalPrice string `json:"originalPrice" dc:"原始价格"` + CancelTime string `json:"cancelTime,omitempty" dc:"订阅取消时间"` +} diff --git a/package/pay/honor/notification.go b/package/pay/honor/notification.go new file mode 100644 index 0000000..2259ab9 --- /dev/null +++ b/package/pay/honor/notification.go @@ -0,0 +1,26 @@ +package honor + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" + "net/http" +) + +func (p *Pay) Notification(r *http.Request) { + +} + +// ConsumeProduct 商品消耗 +func (p *Pay) ConsumeProduct(purchaseToken string) (err error) { + url := Host + "/iap/server/consumeProduct" + _, err = g.Client().ContentJson().Post(gctx.New(), url, g.Map{ + "purchaseToken": purchaseToken, + "developerChallenge": "", + }) + if err != nil { + + return + } + + return +}