From 12a193fdee143ef9da8d319401706de19201b5f0 Mon Sep 17 00:00:00 2001 From: ayflying Date: Wed, 9 Jul 2025 11:01:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0oppo=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package/pay/oppo/model.go | 13 +++++ package/pay/oppo/oppo.go | 119 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 package/pay/oppo/model.go create mode 100644 package/pay/oppo/oppo.go diff --git a/package/pay/oppo/model.go b/package/pay/oppo/model.go new file mode 100644 index 0000000..41e4227 --- /dev/null +++ b/package/pay/oppo/model.go @@ -0,0 +1,13 @@ +package oppo + +// OPPO支付回调参数结构体 +type PayCallback struct { + NotifyId string `json:"notifyId" dc:"回调通知单号,以GC开头,必填,示例:GC20230314657000"` + PartnerOrder string `json:"partnerOrder" dc:"开发者订单号,必填,示例:123456"` + ProductName string `json:"productName" dc:"商品名称,必填,示例:10元宝"` + ProductDesc string `json:"productDesc" dc:"商品描述,必填,示例:10元宝等于1元"` + Price int64 `json:"price" dc:"商品价格,单位为分,需要游戏服务端做验证,必填,示例:100"` + Count int `json:"count" dc:"商品数量(一般为1),必填,示例:1"` + Attach string `json:"attach" dc:"请求支付时上传的附加参数,可能为空,选填"` + Sign string `json:"sign" dc:"OPPO服务端签名,需要游戏服务端做验证,必填"` +} diff --git a/package/pay/oppo/oppo.go b/package/pay/oppo/oppo.go new file mode 100644 index 0000000..da204ed --- /dev/null +++ b/package/pay/oppo/oppo.go @@ -0,0 +1,119 @@ +package oppo + +import ( + "context" + "crypto" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "net/http" + "strings" +) + +// 跟充值平台通信的加密key +//const PUBLIC_KEY = `dfsdfs` + +type OppoType struct { + PublicKey string `json:"public_key"` +} + +func New(PublicKey string) *OppoType { + + return &OppoType{ + PublicKey: PublicKey, + } +} + +func (p *OppoType) Verify(ctx context.Context, data map[string]string) error { + // 解析请求参数 + for k, v := range data { + if v == "" || v == "0" { + delete(data, k) + } + } + + //data["notifyId"] = getParam(r, "notifyId") + //data["partnerOrder"] = getParam(r, "partnerOrder") + //data["productName"] = getParam(r, "productName") + //data["productDesc"] = getParam(r, "productDesc") + //data["price"] = getParam(r, "price") + //data["count"] = getParam(r, "count") + //data["attach"] = getParam(r, "attach") + //data["sign"] = getParam(r, "sign") + + // 验证签名 + result, err := p.rsaVerify(data) + if err != nil { + //http.Error(w, "Verification error: "+err.Error(), http.StatusInternalServerError) + g.Log().Errorf(ctx, "Verification error: %v", err.Error()) + return err + } + + if result { + // TODO::验证成功,处理后续逻辑 + //fmt.Fprint(w, "Verification successful") + //g.Log().Errorf(ctx, "Verification error: %v", err.Error()) + } else { + // TODO::验证失败,处理后续逻辑 + //http.Error(w, "Verification failed", http.StatusBadRequest) + g.Log().Error(ctx, "Verification failed") + err = gerror.New("Verification failed") + } + return nil +} + +func (p *OppoType) getParam(r *http.Request, paramName string) string { + r.ParseForm() + if value := r.FormValue(paramName); value != "" { + return strings.TrimSpace(value) + } + return "" +} + +func (p *OppoType) rsaVerify(contents map[string]string) (bool, error) { + // 构建待签名字符串 + strContents := fmt.Sprintf("notifyId=%s&partnerOrder=%s&productName=%s&productDesc=%s&price=%s&count=%s&attach=%s", + contents["notifyId"], contents["partnerOrder"], contents["productName"], + contents["productDesc"], contents["price"], contents["count"], contents["attach"]) + + // 解析公钥 + publicKey := p.PublicKey + pemData := []byte("-----BEGIN PUBLIC KEY-----\n" + + strings.ReplaceAll(publicKey, " ", "\n") + + "\n-----END PUBLIC KEY-----") + + block, _ := pem.Decode(pemData) + if block == nil { + return false, fmt.Errorf("failed to decode PEM block") + } + + pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return false, err + } + + pubKey, ok := pubInterface.(*rsa.PublicKey) + if !ok { + return false, fmt.Errorf("public key is not an RSA public key") + } + + // 解码签名 + signature, err := base64.StdEncoding.DecodeString(contents["sign"]) + if err != nil { + return false, err + } + + // 计算内容的哈希值 + hash := sha1.New() + hash.Write([]byte(strContents)) + hashed := hash.Sum(nil) + + // 验证签名 + err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA1, hashed, signature) + return err == nil, err +}