插件-支付宝红包转账,签名,证书加载,加密处理
This commit is contained in:
parent
b78c50e72e
commit
b40c2ad29e
6
go.mod
6
go.mod
|
@ -20,10 +20,10 @@ require (
|
|||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
|
||||
github.com/oklog/run v1.0.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/sys v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
google.golang.org/grpc v1.64.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
|
|
12
go.sum
12
go.sum
|
@ -46,16 +46,16 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
module plugins/alipay_redpack
|
||||
|
||||
go 1.22.2
|
||||
|
||||
replace plugins/utils => ../../utils
|
||||
|
||||
require (
|
||||
gitea.cdlsxd.cn/BaseSystem/plugin v0.0.0-20240808015117-a7d6a128aae4
|
||||
github.com/carlmjohnson/requests v0.23.4
|
||||
github.com/go-playground/validator/v10 v10.22.0
|
||||
github.com/hashicorp/go-plugin v1.6.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
plugins/utils v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fatih/color v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/hashicorp/go-hclog v0.14.1 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
|
||||
github.com/oklog/run v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/net v0.23.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
google.golang.org/grpc v1.64.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -0,0 +1,73 @@
|
|||
gitea.cdlsxd.cn/BaseSystem/plugin v0.0.0-20240808015117-a7d6a128aae4 h1:0xL8Kufk3Ka75JpctzFHHyr2gJ8fvsFysOihPSyW3fo=
|
||||
gitea.cdlsxd.cn/BaseSystem/plugin v0.0.0-20240808015117-a7d6a128aae4/go.mod h1:59zYRFcPur2HizJEIfttVOMY0AscollDYKX0yvPJS7k=
|
||||
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
|
||||
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
|
||||
github.com/carlmjohnson/requests v0.23.4 h1:AxcvapfB9RPXLSyvAHk9YJoodQ43ZjzNHj6Ft3tQGdg=
|
||||
github.com/carlmjohnson/requests v0.23.4/go.mod h1:Qzp6tW4DQyainPP+tGwiJTzwxvElTIKm0B191TgTtOA=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
|
||||
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI=
|
||||
github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
|
||||
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,94 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/proto"
|
||||
"github.com/carlmjohnson/requests"
|
||||
"plugins/alipay_redpack/internal/po"
|
||||
)
|
||||
|
||||
// 插件通信信息,若不对应则会报错panic
|
||||
const (
|
||||
Tag = "alipay_redpack"
|
||||
Version = 1
|
||||
CookieKey = "alipay_redpack"
|
||||
CookieValue = "alipay_redpack"
|
||||
)
|
||||
|
||||
const baseUri = "https://openapi.alipay.com/gateway.do"
|
||||
|
||||
const (
|
||||
orderMethod = "alipay.fund.trans.uni.transfer"
|
||||
queryMethod = "alipay.fund.trans.common.query"
|
||||
)
|
||||
|
||||
type AlipayCpnService struct{}
|
||||
|
||||
func (s *AlipayCpnService) Order(ctx context.Context, request *proto.OrderRequest) (*proto.OrderResponse, error) {
|
||||
c, err := transConfig(request.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
poReq, err := orderReq(request.Order, request.Product)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
param, err := c.paramReq(poReq, orderMethod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uv, err := req(c, param)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response po.OrderResp
|
||||
err = requests.URL(c.BaseUri).BodyForm(uv).ToJSON(&response).Fetch(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("请求异常,msg:" + err.Error())
|
||||
}
|
||||
|
||||
return orderResp(request, response), nil
|
||||
}
|
||||
|
||||
func (s *AlipayCpnService) Query(ctx context.Context, request *proto.QueryRequest) (*proto.QueryResponse, error) {
|
||||
c, err := transConfig(request.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
poReq, err := queryReq(request.Order)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
param, err := c.paramReq(poReq, queryMethod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uv, err := req(c, param)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response po.QueryResp
|
||||
err = requests.URL(c.BaseUri).BodyForm(uv).ToJSON(&response).Fetch(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("请求异常,msg:" + err.Error())
|
||||
}
|
||||
return queryResp(request, response), nil
|
||||
}
|
||||
|
||||
func (s *AlipayCpnService) Notify(_ context.Context, request *proto.NotifyRequest) (*proto.NotifyResponse, error) {
|
||||
c, err := transConfig(request.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n := notifyReq(request)
|
||||
|
||||
b, err := Verify(n, c.Npk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !b {
|
||||
return nil, fmt.Errorf("验签失败")
|
||||
}
|
||||
return notifyResp(n), nil
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/proto"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var server = &AlipayCpnService{}
|
||||
|
||||
func config() []byte {
|
||||
c := &Config{
|
||||
AppId: "2021004100663111",
|
||||
Prk: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDbA+YuMp4JUVj6rjzgwGKNXWkEMGX/rinqkfyBZ6B6p8EKz8zgA+ypiJLOixD3GyKnUnAzx4waNRZHfdEu+l57kJFtd/ipfwtJ28aTi7TqtqEpqD+UPY4ourt2CuyCFxWsonS6dczqtTvfAVArTdbGJYY+kNNVR3WiXgGUhkUu8N7vEowU00RUQGNdSVMUs4FX+HlU3RnEoRc/xUhPiaLf0Bm/g9wG96kwyg/TZvkNU7PpMVRdXeLrVORn0qThs3VA4dqondF+O12iC1TK4TKYGzFYczGAUsfuurtDyCc2GMoE+hH2FR8U7amQOVuYZFkutTdqaqukWpFQOr8wLeMzAgMBAAECggEAD715/3v3y6ejA3EeQvDQpGRANeLckcGMlaUkRpCSAf6oawSALuFZUt3T3zAzae7zUJ8mHTKMKR4DmeO68utfevXq3bkvj87nmslGvjfeKrgxYPMMjrTV0KuK6XLjiH3sOtn6FaR9s6iOwvovLs2LT/ZGbZyu84QNOjwTVP9JXZQkBgMItdKf+U3H2Cjp7U/qXBt8/9yzVFklp1g1883DAty0lzmT27dJimGVGaPQ8vNxo81+ZUEJAn6GUTk0K/GwJfhPTU8hh8G90n2LTyskoMjGxQe9lXfCcS9DmWawEQL4WTctPrDYlnS/cjCVMS0KXIFuxRNf6qaMYDeywC8BgQKBgQDyf40dvmw34Rrb46+NLayQ9W5CJI/dYeRajpCjoOomq5QYhbXzUCpVfbtByeGMsg3zN58NNsZGhl5SU0GdEdoOlxCk+2Hey2yQYF4ugQm/dTd68Jgqi3yujigAbNYa1ZhL9t3FqouPY1dGiaxl+DFYdSMIrsVFXh2NbrPyqTk5IQKBgQDnNaH7LCcIUqg9H8Tsls+8GLzP2HwF3hdll8asEsF3K3HX6/Zlp4VnnEcIkAxLRL/L0o5akXrmA18ZwfoSguTPXV9va3G2GgIiJHgcytmGtQVvbpFKuPnCXKz+avxnfO0flJqyYEuHr/40jsGbMkk0Kr52/n3ivXZbBUlT+tkt0wKBgB0qLgSnxEgsMJjFl3V5SsncWrhlwU+02Evz3X1wevjPpe4VFr7+ozjI+F5/MztCpt7bj6t9LPeKbYmlLb0ASqN6k6vj9+9ds97hWDJrnoqCRHvqt8JWKFauDi2O6WksyzZHqIB/dG14WyTGpg9VfEnRPLdsnZksKo26BLZol9NBAoGBAMz7oMNljqlzVtLyMo2q2zuhFuySusoc79NTL4FpE3rK2qCbA5V2YvDL/bIau7uTlRNodmrXZgU84fidIE9/Gsq5tp26vVK8Vj3c5Vxpf1dNcCct+MQtoMjvjzP0uBgsCrKf9lLEytHed1ozYnRsrbgBWWF4GTWH0cG6uxsoX5mfAoGAfYXKZdyRU+Su4y4EnDLMXd320ar7PaeuY8aZU7V6UQEsaOj6H3O8JMEOuBrOVEhAP2EkAC8ayargSXTSOkN97pg88agKDwA6jh4N6TKAK8XMft81YPPliVwZMsAUqihSKQBnKZ7ssHHLGWWWp5vfkyb7Y7dIkZcPzB0X3q/jL58=",
|
||||
Npk: "",
|
||||
MchCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt",
|
||||
RootCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt",
|
||||
}
|
||||
marshal, _ := json.Marshal(c)
|
||||
return marshal
|
||||
}
|
||||
|
||||
func TestOrder(t *testing.T) {
|
||||
request := &proto.OrderRequest{
|
||||
Config: config(),
|
||||
Order: &proto.OrderRequest_Order{
|
||||
OrderNo: "240403164049635931",
|
||||
Account: "18666173766",
|
||||
Extra: []byte(``),
|
||||
},
|
||||
Product: &proto.OrderRequest_Product{
|
||||
ProductNo: "",
|
||||
Price: 0.01,
|
||||
Extra: []byte(`{"wishing":"祝福语"}`),
|
||||
},
|
||||
}
|
||||
t.Run("TestOrder", func(t *testing.T) {
|
||||
got, err := server.Order(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Errorf("Order() error = %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%+v", got)
|
||||
assert.Equal(t, int(proto.Status_ING), int(got.Result.Status))
|
||||
})
|
||||
}
|
||||
|
||||
func TestQuery(t *testing.T) {
|
||||
request := &proto.QueryRequest{
|
||||
Config: config(),
|
||||
Order: &proto.QueryRequest_Order{
|
||||
OrderNo: "lsxd202306071545141532",
|
||||
TradeNo: "20230607110070000006320086925897",
|
||||
Account: "",
|
||||
Extra: []byte(``),
|
||||
},
|
||||
}
|
||||
t.Run("TestQuery", func(t *testing.T) {
|
||||
got, err := server.Query(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Errorf("Query() error = %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%+v \n", got)
|
||||
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNotify(t *testing.T) {
|
||||
in := &proto.NotifyRequest{
|
||||
Config: config(),
|
||||
Queries: []byte(``),
|
||||
Headers: []byte(``),
|
||||
Body: []byte(``),
|
||||
}
|
||||
t.Run("TestNotify", func(t *testing.T) {
|
||||
got, err := server.Notify(context.Background(), in)
|
||||
if err != nil {
|
||||
t.Errorf("Notify() error = %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("TestNotify : %+v \n", got)
|
||||
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
|
||||
})
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package po
|
||||
|
||||
import "plugins/alipay_redpack/internal/vo"
|
||||
|
||||
type NotifyBizContent struct {
|
||||
OutBizNo string `json:"out_biz_no"`
|
||||
ProductCode string `json:"product_code"`
|
||||
BizScene string `json:"biz_scene"`
|
||||
OriginInterface string `json:"origin_interface"`
|
||||
OrderId string `json:"order_id"`
|
||||
Status vo.Status `json:"status"`
|
||||
ActionType string `json:"action_type"`
|
||||
TransAmount string `json:"trans_amount"`
|
||||
SettleSerialNo string `json:"settle_serial_no"`
|
||||
SubOrderStatus string `json:"sub_order_status"`
|
||||
SubOrderErrorCode string `json:"sub_order_error_code"`
|
||||
SubOrderFailReason string `json:"sub_order_fail_reason"`
|
||||
PayFundOrderId string `json:"pay_fund_order_id"`
|
||||
PayDate string `json:"pay_date"`
|
||||
RefundDate string `json:"refund_date"`
|
||||
EntrustOrderId string `json:"entrust_order_id"`
|
||||
}
|
||||
|
||||
type Notify struct {
|
||||
Charset string `json:"charset"`
|
||||
UtcTimestamp string `json:"utc_timestamp"`
|
||||
AppId string `json:"app_id"`
|
||||
Version string `json:"version"`
|
||||
SignType string `json:"sign_type"`
|
||||
NotifyId string `json:"notify_id"`
|
||||
MsgMethod string `json:"msg_method"`
|
||||
Sign string `json:"sign"`
|
||||
BizContent string `json:"biz_content"`
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package po
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/proto"
|
||||
"plugins/alipay_redpack/internal/vo"
|
||||
)
|
||||
|
||||
type PayeeInfo struct {
|
||||
Identity string `validate:"required" json:"identity"`
|
||||
IdentityType string `validate:"required" json:"identity_type"`
|
||||
}
|
||||
|
||||
type BusinessParams struct {
|
||||
SubBizScene string `json:"sub_biz_scene"`
|
||||
}
|
||||
|
||||
func (b *BusinessParams) ToString() string {
|
||||
s, _ := json.Marshal(b)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type OrderReq struct {
|
||||
OutBizNo string `validate:"required" json:"out_biz_no"`
|
||||
TransAmount string `validate:"required" json:"trans_amount"`
|
||||
ProductCode string `validate:"required" json:"product_code"`
|
||||
BizScene string `validate:"required" json:"biz_scene"`
|
||||
OrderTitle string `json:"order_title"`
|
||||
Remark string `json:"remark"`
|
||||
PayeeInfo *PayeeInfo `json:"payee_info"`
|
||||
BusinessParams string `json:"business_params"`
|
||||
}
|
||||
|
||||
type OrderResponse struct {
|
||||
Code vo.Code `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
SubCode string `json:"sub_code"`
|
||||
SubMsg string `json:"sub_msg"`
|
||||
|
||||
OutBizNo string `json:"out_biz_no"`
|
||||
OrderId string `json:"order_id"`
|
||||
PayFundOrderId string `json:"pay_fund_order_id"`
|
||||
Status string `json:"status"`
|
||||
TransDate int32 `json:"trans_date"`
|
||||
SettleSerialNo int32 `json:"settle_serial_no"`
|
||||
}
|
||||
|
||||
type OrderResp struct {
|
||||
Response OrderResponse `json:"alipay_fund_trans_uni_transfer_response"`
|
||||
Sign string `json:"sign"`
|
||||
}
|
||||
|
||||
func (o *OrderResp) GetMsg() string {
|
||||
if o.Response.Code.IsSuccess() {
|
||||
return o.Response.Msg
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"code:[%s],msg:[%s],subCode:[%s],subMsg[%s]",
|
||||
o.Response.Code, o.Response.Msg, o.Response.SubCode, o.Response.SubMsg,
|
||||
)
|
||||
}
|
||||
|
||||
func (o *OrderResp) GetOrderStatus() proto.Status {
|
||||
if o.Response.Code.IsSuccess() {
|
||||
return proto.Status_ING
|
||||
}
|
||||
return proto.Status_FAIL
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package po
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type Param struct {
|
||||
AlipayRootCertSn string `json:"alipay_root_cert_sn" validate:"required"`
|
||||
AppCertSn string `json:"app_cert_sn" validate:"required"`
|
||||
AppId string `json:"app_id" validate:"required"`
|
||||
Method string `json:"method" validate:"required"`
|
||||
Format string `json:"format" validate:"required"`
|
||||
Charset string `json:"charset" validate:"required"`
|
||||
SignType string `json:"sign_type" validate:"required"`
|
||||
Timestamp string `json:"timestamp" validate:"required"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
BizContent string `json:"biz_content"`
|
||||
Sign string `json:"sign"`
|
||||
}
|
||||
|
||||
type Req interface {
|
||||
Validate() error
|
||||
ToJson() []byte
|
||||
}
|
||||
|
||||
var _ Req = (*OrderReq)(nil)
|
||||
var _ Req = (*QueryReq)(nil)
|
||||
var _ Req = (*Notify)(nil)
|
||||
|
||||
func (req *OrderReq) Validate() error {
|
||||
err := validator.New().Struct(req)
|
||||
if err != nil {
|
||||
for _, err = range err.(validator.ValidationErrors) {
|
||||
return fmt.Errorf("参数有误:" + err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (req *OrderReq) ToJson() []byte {
|
||||
b, _ := json.Marshal(req)
|
||||
return b
|
||||
}
|
||||
|
||||
func (req *QueryReq) Validate() error {
|
||||
err := validator.New().Struct(req)
|
||||
if err != nil {
|
||||
for _, err = range err.(validator.ValidationErrors) {
|
||||
return fmt.Errorf("参数有误:" + err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (req *QueryReq) ToJson() []byte {
|
||||
b, _ := json.Marshal(req)
|
||||
return b
|
||||
}
|
||||
|
||||
func (req *Notify) Validate() error {
|
||||
err := validator.New().Struct(req)
|
||||
if err != nil {
|
||||
for _, err = range err.(validator.ValidationErrors) {
|
||||
return fmt.Errorf("参数有误:" + err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (req *Notify) ToJson() []byte {
|
||||
b, _ := json.Marshal(req)
|
||||
return b
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package po
|
||||
|
||||
import (
|
||||
"plugins/alipay_redpack/internal/vo"
|
||||
)
|
||||
|
||||
type QueryReq struct {
|
||||
OutBizNo string `json:"out_biz_no"`
|
||||
OrderId string `json:"order_id"`
|
||||
ProductCode string `json:"product_code"`
|
||||
BizScene string `json:"biz_scene"`
|
||||
}
|
||||
|
||||
type QueryResp struct {
|
||||
Data QueryRespData `json:"alipay_fund_trans_common_query_response"`
|
||||
Sign string `json:"sign"`
|
||||
}
|
||||
|
||||
type QueryRespData struct {
|
||||
Code vo.Code `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
SubCode string `json:"subCode"`
|
||||
SubMsg string `json:"subMsg"`
|
||||
|
||||
OrderId string `json:"order_id"`
|
||||
OutBizNo string `json:"out_biz_no"`
|
||||
Status vo.Status `json:"status"`
|
||||
PayFundOrderId string `json:"pay_fund_order_id"`
|
||||
TransAmount string `json:"trans_amount"`
|
||||
PayDate string `json:"pay_date"`
|
||||
ArrivalTimeEnd string `json:"arrival_time_end"`
|
||||
OrderFee string `json:"order_fee"`
|
||||
ErrorCode string `json:"error_code"`
|
||||
FailReason string `json:"fail_reason"`
|
||||
SubOrderErrorCode string `json:"sub_order_error_code"`
|
||||
SubOrderFailReason string `json:"sub_order_fail_reason"`
|
||||
SubOrderStatus string `json:"sub_order_status"`
|
||||
SettleSerialNo string `json:"settle_serial_no"`
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/proto"
|
||||
"html"
|
||||
"plugins/alipay_redpack/internal/po"
|
||||
"plugins/alipay_redpack/internal/vo"
|
||||
"plugins/utils/alipay"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
AppId string `json:"app_id"`
|
||||
BaseUri string `json:"base_uri"`
|
||||
Prk string `json:"prk"` // 私钥
|
||||
Npk string `json:"npk"` // 回调公钥
|
||||
|
||||
MchCertPath string `json:"mch_cert_path"`
|
||||
RootCertPath string `json:"root_cert_path"`
|
||||
}
|
||||
|
||||
func transConfig(config []byte) (*Config, error) {
|
||||
var c Config
|
||||
err := json.Unmarshal(config, &c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.BaseUri == "" {
|
||||
c.BaseUri = baseUri
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (c *Config) paramReq(req po.Req, method string) (*po.Param, error) {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert, err := alipay.GetCert(c.MchCertPath, c.RootCertPath, c.AppId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &po.Param{
|
||||
AlipayRootCertSn: cert.RootCertSN,
|
||||
AppCertSn: cert.MchCertSN,
|
||||
AppId: c.AppId,
|
||||
Method: method,
|
||||
Format: vo.Format,
|
||||
Charset: vo.Charset,
|
||||
SignType: vo.SignType,
|
||||
Timestamp: time.Now().Format(time.DateTime),
|
||||
Version: vo.Version,
|
||||
BizContent: string(req.ToJson()),
|
||||
Sign: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func orderReq(order *proto.OrderRequest_Order, product *proto.OrderRequest_Product) (*po.OrderReq, error) {
|
||||
type Extra struct {
|
||||
Wishing string `json:"wishing"`
|
||||
}
|
||||
var extra Extra
|
||||
if product.Extra != nil {
|
||||
err := json.Unmarshal(product.Extra, &extra)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("product extra json unmarshal error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
payeeInfo := &po.PayeeInfo{
|
||||
Identity: order.Account,
|
||||
IdentityType: "ALIPAY_USER_ID",
|
||||
}
|
||||
if isValidPhoneNumber(order.Account) {
|
||||
payeeInfo.IdentityType = "ALIPAY_LOGON_ID"
|
||||
}
|
||||
businessParams := &po.BusinessParams{
|
||||
SubBizScene: "REDPACKET",
|
||||
}
|
||||
o := &po.OrderReq{
|
||||
OutBizNo: order.OrderNo,
|
||||
TransAmount: fmt.Sprintf("%.2f", product.Price),
|
||||
ProductCode: product.ProductNo,
|
||||
BizScene: "DIRECT_TRANSFER",
|
||||
OrderTitle: extra.Wishing,
|
||||
Remark: extra.Wishing,
|
||||
PayeeInfo: payeeInfo,
|
||||
BusinessParams: businessParams.ToString(),
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func orderResp(request *proto.OrderRequest, resp po.OrderResp) *proto.OrderResponse {
|
||||
data, _ := json.Marshal(resp)
|
||||
return &proto.OrderResponse{
|
||||
Result: &proto.Result{
|
||||
Status: resp.GetOrderStatus(),
|
||||
OrderNo: request.Order.OrderNo,
|
||||
TradeNo: resp.Response.OrderId,
|
||||
Message: resp.GetMsg(),
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func queryReq(in *proto.QueryRequest_Order) (*po.QueryReq, error) {
|
||||
return &po.QueryReq{
|
||||
OutBizNo: in.OrderNo,
|
||||
OrderId: in.TradeNo,
|
||||
ProductCode: "STD_RED_PACKET",
|
||||
BizScene: "DIRECT_TRANSFER",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func queryResp(request *proto.QueryRequest, resp po.QueryResp) *proto.QueryResponse {
|
||||
data, _ := json.Marshal(resp)
|
||||
return &proto.QueryResponse{
|
||||
Result: &proto.Result{
|
||||
Status: resp.Data.Status.GetOrderStatus(),
|
||||
OrderNo: request.Order.OrderNo,
|
||||
TradeNo: request.Order.TradeNo,
|
||||
Message: resp.Data.Msg,
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func notifyReq(in *proto.NotifyRequest) *po.Notify {
|
||||
var n *po.Notify
|
||||
_ = json.Unmarshal(in.Queries, &n)
|
||||
n.BizContent = html.UnescapeString(n.BizContent)
|
||||
return n
|
||||
}
|
||||
|
||||
func notifyResp(n *po.Notify) *proto.NotifyResponse {
|
||||
var b po.NotifyBizContent
|
||||
_ = json.Unmarshal([]byte(n.BizContent), &b)
|
||||
return &proto.NotifyResponse{
|
||||
Result: &proto.Result{
|
||||
Status: b.Status.GetOrderStatus(),
|
||||
OrderNo: b.OutBizNo,
|
||||
TradeNo: b.OrderId,
|
||||
Message: b.Status.GetText(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/utils"
|
||||
"net/url"
|
||||
"plugins/alipay_redpack/internal/po"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func isValidPhoneNumber(phoneNumber string) bool {
|
||||
// 定义中国大陆手机号的正则表达式
|
||||
// 这个正则表达式匹配11位数字,以1开头,第二位为3、4、5、7、8中的一个,后面跟任意8位数字
|
||||
phoneRegex := `^1[34578]\d{9}$`
|
||||
// 使用正则表达式进行匹配
|
||||
return regexp.MustCompile(phoneRegex).MatchString(phoneNumber)
|
||||
}
|
||||
|
||||
func req(config *Config, req *po.Param) (url.Values, error) {
|
||||
var strToBeSigned strings.Builder
|
||||
uv := url.Values{}
|
||||
kvRows := utils.SortStructJsonTag(req)
|
||||
for _, kv := range kvRows {
|
||||
if kv.Key == "sign" {
|
||||
continue
|
||||
}
|
||||
if kv.Value == "" {
|
||||
continue
|
||||
}
|
||||
uv.Set(kv.Key, kv.Value)
|
||||
strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
|
||||
}
|
||||
s := strings.TrimRight(strToBeSigned.String(), "&")
|
||||
sign, err := Sign(s, []byte(utils.NewPrivate().Build(config.Prk)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uv.Set("sign", sign)
|
||||
|
||||
return uv, nil
|
||||
}
|
||||
|
||||
func Sign(data string, privateKeyPEM []byte) (string, error) {
|
||||
block, _ := pem.Decode(privateKeyPEM)
|
||||
if block == nil {
|
||||
return "", errors.New("failed to parse PEM block containing the private key")
|
||||
}
|
||||
|
||||
privyKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse DER encoded private key: %v", err)
|
||||
}
|
||||
|
||||
hashed := sha256.Sum256([]byte(data))
|
||||
signature, err := rsa.SignPKCS1v15(rand.Reader, privyKey.(*rsa.PrivateKey), crypto.SHA256, hashed[:])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to sign: %v", err)
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(signature), nil
|
||||
}
|
||||
|
||||
func Verify(n *po.Notify, publicKeyPEM string) (bool, error) {
|
||||
var strToBeSigned strings.Builder
|
||||
uv := url.Values{}
|
||||
kvRows := utils.SortStructJsonTag(n)
|
||||
for _, kv := range kvRows {
|
||||
if kv.Key == "sign" {
|
||||
continue
|
||||
}
|
||||
if kv.Key == "sign_type" {
|
||||
continue
|
||||
}
|
||||
uv.Set(kv.Key, kv.Value)
|
||||
strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
|
||||
}
|
||||
s := strings.TrimRight(strToBeSigned.String(), "&")
|
||||
return check(s, n.Sign, []byte(utils.NewPublic().Build(publicKeyPEM)))
|
||||
}
|
||||
|
||||
func check(data, signature string, publicKeyPEM []byte) (bool, error) {
|
||||
block, _ := pem.Decode(publicKeyPEM)
|
||||
if block == nil {
|
||||
return false, fmt.Errorf("failed to parse DER encoded public key")
|
||||
}
|
||||
|
||||
pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to parse DER encoded public key: %v", err)
|
||||
}
|
||||
|
||||
rsaPubKey, ok := pubKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("failed to parse DER encoded public key: %v", err)
|
||||
}
|
||||
|
||||
hashed := sha256.Sum256([]byte(data))
|
||||
sig, err := base64.StdEncoding.DecodeString(signature)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to decode signature: %v", err)
|
||||
}
|
||||
|
||||
err = rsa.VerifyPKCS1v15(rsaPubKey, crypto.SHA256, hashed[:], sig)
|
||||
if err != nil {
|
||||
fmt.Println("signature verification error:", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package vo
|
||||
|
||||
type Code string
|
||||
|
||||
const CodeSuccess Code = "10000"
|
||||
|
||||
func (c Code) IsSuccess() bool {
|
||||
return c == CodeSuccess
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package vo
|
||||
|
||||
const (
|
||||
Version = "1.0"
|
||||
Format = "json"
|
||||
SignType = "RSA2"
|
||||
Charset = "UTF-8"
|
||||
)
|
|
@ -0,0 +1,44 @@
|
|||
package vo
|
||||
|
||||
import "gitea.cdlsxd.cn/BaseSystem/plugin/proto"
|
||||
|
||||
type Status string
|
||||
|
||||
const (
|
||||
QueryStatusPending = "DEALING"
|
||||
QueryStatusSuccess = "SUCCESS"
|
||||
QueryStatusFailed = "FAIL"
|
||||
QueryStatusRefund = "REFUND"
|
||||
)
|
||||
|
||||
var queryStatusTextMap = map[Status]string{
|
||||
QueryStatusPending: "处理中",
|
||||
QueryStatusSuccess: "转账成功",
|
||||
QueryStatusFailed: "失败",
|
||||
QueryStatusRefund: "退票",
|
||||
}
|
||||
|
||||
var queryStatusMap = map[Status]proto.Status{
|
||||
QueryStatusPending: proto.Status_ING,
|
||||
QueryStatusSuccess: proto.Status_SUCCESS,
|
||||
QueryStatusFailed: proto.Status_FAIL,
|
||||
QueryStatusRefund: proto.Status_FAIL,
|
||||
}
|
||||
|
||||
func (o Status) GetText() string {
|
||||
msg, ok := queryStatusTextMap[o]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
func (o Status) GetOrderStatus() proto.Status {
|
||||
if o == "" {
|
||||
return proto.Status_INVALID
|
||||
}
|
||||
if resultStatus, ok := queryStatusMap[o]; ok {
|
||||
return resultStatus
|
||||
}
|
||||
return proto.Status_FAIL
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package vo
|
||||
|
||||
type VoucherStatus string
|
||||
|
||||
const (
|
||||
VoucherStatusEnabled VoucherStatus = "ENABLED"
|
||||
VoucherStatusDisabled VoucherStatus = "DISABLED"
|
||||
)
|
||||
|
||||
var voucherStatusTextMap = map[VoucherStatus]string{
|
||||
VoucherStatusEnabled: "券状态:可用",
|
||||
VoucherStatusDisabled: "券状态:不可用",
|
||||
}
|
||||
|
||||
func (o VoucherStatus) GetText() string {
|
||||
msg, ok := voucherStatusTextMap[o]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return msg
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/shared"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"plugins/alipay_redpack/internal"
|
||||
)
|
||||
|
||||
func main() {
|
||||
plugin.Serve(&plugin.ServeConfig{
|
||||
HandshakeConfig: shared.HandshakeConfig(internal.Version, internal.CookieKey, internal.CookieValue),
|
||||
Plugins: shared.PluginSet(shared.NewPlugin(&internal.AlipayCpnService{}, internal.Tag)),
|
||||
GRPCServer: plugin.DefaultGRPCServer,
|
||||
})
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package alipay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type CertConfig struct {
|
||||
MchCertSN string
|
||||
RootCertSN string
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
once sync.Once
|
||||
mutex sync.RWMutex
|
||||
CertConfigs map[string]*CertConfig
|
||||
}
|
||||
|
||||
var instance manager
|
||||
|
||||
func getCertConfig(appId string) *CertConfig {
|
||||
c, ok := instance.CertConfigs[appId]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func setCertConfig(appId string, certConfig *CertConfig) {
|
||||
instance.mutex.Lock()
|
||||
defer instance.mutex.Unlock()
|
||||
instance.CertConfigs[appId] = certConfig
|
||||
}
|
||||
|
||||
func init() {
|
||||
instance.CertConfigs = make(map[string]*CertConfig)
|
||||
}
|
||||
|
||||
func GetCert(mchCertPath, rootCertPath, appId string) (*CertConfig, error) {
|
||||
if mchCertPath == "" || rootCertPath == "" || appId == "" {
|
||||
return nil, fmt.Errorf("mchCertPath or rootCertPath or appId is empty")
|
||||
}
|
||||
c := getCertConfig(appId)
|
||||
if c != nil {
|
||||
return nil, fmt.Errorf("appId %s already exists", appId)
|
||||
}
|
||||
mchCertSN, err := GetMchCertSN(mchCertPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get mchCertSN error: %v", err)
|
||||
}
|
||||
rootCertSN, err := GetRootCertSN(rootCertPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get rootCertSN error: %v", err)
|
||||
}
|
||||
c = &CertConfig{
|
||||
MchCertSN: mchCertSN,
|
||||
RootCertSN: rootCertSN,
|
||||
}
|
||||
setCertConfig(appId, c)
|
||||
return c, nil
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package alipay
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCertSN(t *testing.T) {
|
||||
ms, err := GetMchCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
//78a57140055b8e7853b1576bcf763361
|
||||
//78a57140055b8e7853b1576bcf763361
|
||||
t.Logf("merchantCertSN:%s", ms)
|
||||
}
|
||||
rs, err := GetRootCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
//687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6
|
||||
//687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6
|
||||
t.Logf("alipayRootCertSN:%s", rs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMchCertSN(t *testing.T) {
|
||||
s, err := GetMchCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
//78a57140055b8e7853b1576bcf763361
|
||||
t.Log(s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootCertSN(t *testing.T) {
|
||||
s, err := GetRootCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Log(s)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package alipay
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// md5Hash 计算 MD5 哈希值
|
||||
func md5Hash(s string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(s))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// hex2dec 将字节数组转换为十六进制字符串
|
||||
func hex2dec(hex string) string {
|
||||
var dec int
|
||||
fmt.Sscanf(hex, "%x", &dec)
|
||||
return fmt.Sprintf("%d", dec)
|
||||
}
|
||||
|
||||
func getCert(certData []byte) (*x509.Certificate, error) {
|
||||
block, _ := pem.Decode(certData)
|
||||
if block == nil {
|
||||
log.Fatal("Failed to parse PEM block containing the cert")
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate: %v", err)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package alipay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
// GetMchCertSN 提取证书序列号
|
||||
func GetMchCertSN(certPath string) (string, error) {
|
||||
certData, err := ioutil.ReadFile(certPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read the cert file: %s", err)
|
||||
}
|
||||
|
||||
cert, err := getCert(certData)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse certificate: %v", err)
|
||||
}
|
||||
|
||||
return md5Hash(cert.Issuer.ToRDNSequence().String() + cert.SerialNumber.String()), nil
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package alipay
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetRootCertSN 计算证书的序列号并返回最终字符串
|
||||
func GetRootCertSN(certPath string) (string, error) {
|
||||
certData, err := ioutil.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read certificate file: %v", err)
|
||||
}
|
||||
var sn string
|
||||
blocks := strings.Split(string(certData), "-----END CERTIFICATE-----")
|
||||
for _, blockStr := range blocks[:len(blocks)-1] {
|
||||
cert, err := getCert([]byte(strings.TrimSpace(blockStr) + "\n-----END CERTIFICATE-----"))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
serialNumber := cert.SerialNumber.String()
|
||||
if strings.HasPrefix(serialNumber, "0x") {
|
||||
serialNumber = hex2dec(serialNumber[2:])
|
||||
}
|
||||
if cert.SignatureAlgorithm == x509.SHA1WithRSA || cert.SignatureAlgorithm == x509.SHA256WithRSA {
|
||||
hash := md5Hash(cert.Issuer.ToRDNSequence().String() + serialNumber)
|
||||
if sn == "" {
|
||||
sn = hash
|
||||
} else {
|
||||
sn += "_" + hash
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sn, nil
|
||||
}
|
Loading…
Reference in New Issue