From d343257e2394022ba30a580a556cf79755aeab1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E4=BF=8A=E5=AE=8F?= <389838709@qq.com> Date: Thu, 1 Aug 2024 14:46:56 +0800 Subject: [PATCH 01/14] =?UTF-8?q?=E6=94=AF=E4=BB=98=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E4=B8=80=E6=B3=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/constants/common/common.go | 10 ++++ app/data/app.go | 1 + app/data/merchant.go | 1 + app/data/order.go | 11 ++++ app/data/order_log.go | 1 + app/data/pay_channel.go | 14 +++++ app/services/payment_service.go | 102 ++++++++++++++++++++++++++++++++ go.mod | 19 ++++-- go.sum | 45 +++++++++----- 9 files changed, 182 insertions(+), 22 deletions(-) create mode 100644 app/data/app.go create mode 100644 app/data/merchant.go create mode 100644 app/data/order.go create mode 100644 app/data/order_log.go create mode 100644 app/data/pay_channel.go create mode 100644 app/services/payment_service.go diff --git a/app/constants/common/common.go b/app/constants/common/common.go index a3a296b..3604755 100644 --- a/app/constants/common/common.go +++ b/app/constants/common/common.go @@ -4,4 +4,14 @@ const ( TOKEN_PRE = "player_token_" TOKEN_Admin = "Admin_token_" ADMIN_V1 = "/admin/api/v1" + + PAYCHANNEL_WX_JSAPI = 1 // 微信支付JSAPI + PAYCHANNEL_WX_H5 = 2 // 微信支付H5 + PAYCHANNEL_WX_APP = 3 // 微信支付APP + PAYCHANNEL_WX_Native = 4 // 微信支付Native + PAYCHANNEL_WX_Minnin = 5 // 微信支付小程序 + PAYCHANNEL_ALI_H5 = 6 // 支付宝支付H5 + PAYCHANNEL_ALI_Minnin = 7 // 支付宝支付小程序 + PAYCHANNEL_ALI_JSAPI = 8 // 支付宝支付JSAPI + ) diff --git a/app/data/app.go b/app/data/app.go new file mode 100644 index 0000000..0ad59c2 --- /dev/null +++ b/app/data/app.go @@ -0,0 +1 @@ +package data diff --git a/app/data/merchant.go b/app/data/merchant.go new file mode 100644 index 0000000..0ad59c2 --- /dev/null +++ b/app/data/merchant.go @@ -0,0 +1 @@ +package data diff --git a/app/data/order.go b/app/data/order.go new file mode 100644 index 0000000..188550a --- /dev/null +++ b/app/data/order.go @@ -0,0 +1,11 @@ +package data + +import "PaymentCenter/app/models/ordersmodel" + +func GetOrderInfoById(id int64) (orderInfo ordersmodel.Orders, err error) { + _, err = ordersmodel.GetInstance().GetDb().Where("Id = ?", id).Get(&orderInfo) + if err != nil { + return ordersmodel.Orders{}, err + } + return orderInfo, nil +} diff --git a/app/data/order_log.go b/app/data/order_log.go new file mode 100644 index 0000000..0ad59c2 --- /dev/null +++ b/app/data/order_log.go @@ -0,0 +1 @@ +package data diff --git a/app/data/pay_channel.go b/app/data/pay_channel.go new file mode 100644 index 0000000..e8b9eb5 --- /dev/null +++ b/app/data/pay_channel.go @@ -0,0 +1,14 @@ +package data + +import ( + "PaymentCenter/app/models/ordersmodel" + "PaymentCenter/app/models/paychannelmodel" +) + +func GetPayChannelById(id int64) (payChannelInfo paychannelmodel.PayChannel, err error) { + _, err = ordersmodel.GetInstance().GetDb().Where("Id = ?", id).Get(&payChannelInfo) + if err != nil { + return paychannelmodel.PayChannel{}, err + } + return payChannelInfo, nil +} diff --git a/app/services/payment_service.go b/app/services/payment_service.go new file mode 100644 index 0000000..cd1c95c --- /dev/null +++ b/app/services/payment_service.go @@ -0,0 +1,102 @@ +package services + +import ( + "PaymentCenter/app/constants/common" + "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/data" + "context" + "errors" + "github.com/go-pay/gopay" + "github.com/go-pay/gopay/wechat/v3" + "github.com/go-pay/xlog" + "time" +) + +func ActivityGoodsCreateService(orderId int64) (err error) { + // 获取订单信息 + orderInfo, err := data.GetOrderInfoById(orderId) + if err != nil { + return err + } + if orderInfo.PayId == 0 { + return errors.New("缺少订单信息,无法进行支付") + } + // 获取支付渠道 + payChannelInfo, err := data.GetPayChannelById(orderInfo.PayId) + if err != nil { + return err + } + + switch payChannelInfo.ChannelType { + case common.PAYCHANNEL_WX_H5: + //todo 微信H5支付 + wxH5PayInfo() + break + case common.PAYCHANNEL_ALI_H5: + //todo 支付宝H5支付 + aLiH5PayInfo() + break + default: + return errors.New("暂不支持该支付渠道,请后续再使用") + } + return nil +} + +func wxH5PayInfo(c context.Context) { + // NewClientV3 初始化微信客户端 v3 + // mchid:商户ID 或者服务商模式的 sp_mchid + // serialNo:商户证书的证书序列号 + // apiV3Key:apiV3Key,商户平台获取 + // privateKey:私钥 apiclient_key.pem 读取后的内容 + client, err := wechat.NewClientV3(MchId, SerialNo, APIv3Key, PrivateKey) + if err != nil { + return + } + + // 设置微信平台API证书和序列号(推荐开启自动验签,无需手动设置证书公钥等信息) + //client.SetPlatformCert([]byte(""), "") + + // 启用自动同步返回验签,并定时更新微信平台API证书(开启自动验签时,无需单独设置微信平台API证书和序列号) + err = client.AutoVerifySign() + if err != nil { + return + } + + // 自定义配置http请求接收返回结果body大小,默认 10MB + client.SetBodySize(10) // 没有特殊需求,可忽略此配置 + + // 打开Debug开关,输出日志,默认是关闭的 + client.DebugSwitch = gopay.DebugOn + expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) + // 初始化 BodyMap + bm := make(gopay.BodyMap) + bm.Set("sp_appid", "sp_appid"). + Set("sp_mchid", "sp_mchid"). + Set("sub_mchid", "sub_mchid"). + Set("description", "测试Jsapi支付商品"). + Set("out_trade_no", tradeNo). + Set("time_expire", expire). + Set("notify_url", "https://www.fmm.ink"). + SetBodyMap("amount", func(bm gopay.BodyMap) { + bm.Set("total", 1). + Set("currency", "CNY") + }). + SetBodyMap("payer", func(bm gopay.BodyMap) { + bm.Set("sp_openid", "asdas") + }) + + wxRsp, err := client.V3TransactionH5(c, bm) + if err != nil { + xlog.Error(err) + return + } + if wxRsp.Code == errorcode.Success { + xlog.Debugf("wxRsp: %#v", wxRsp.Response) + return + } + xlog.Errorf("wxRsp:%s", wxRsp.Error) +} + +func aLiH5PayInfo() { + +} diff --git a/go.mod b/go.mod index 21c4a1f..e3e41d2 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible github.com/forgoer/openssl v1.6.0 github.com/gin-gonic/gin v1.7.7 + github.com/go-pay/gopay v1.5.103 github.com/go-playground/locales v0.14.0 github.com/go-playground/universal-translator v0.18.0 github.com/go-sql-driver/mysql v1.6.0 @@ -52,11 +53,17 @@ require ( github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect github.com/eapache/queue v1.1.0 // indirect github.com/emirpasic/gods v1.12.0 // indirect + github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.6 // indirect github.com/go-openapi/spec v0.20.4 // indirect github.com/go-openapi/swag v0.19.15 // indirect + github.com/go-pay/crypto v0.0.1 // indirect + github.com/go-pay/errgroup v0.0.2 // indirect + github.com/go-pay/util v0.0.2 // indirect + github.com/go-pay/xlog v0.0.3 // indirect + github.com/go-pay/xtime v0.0.2 // indirect github.com/go-playground/validator/v10 v10.9.0 // indirect github.com/go-redis/redis/v8 v8.11.4 // indirect github.com/goccy/go-json v0.8.1 // indirect @@ -98,13 +105,13 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.21.0 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.1.0 // indirect - golang.org/x/tools v0.6.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index a3fda71..43a6d96 100644 --- a/go.sum +++ b/go.sum @@ -173,6 +173,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc= github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yHzKvwhsiSIWmbAYM3Dr9AEEbER2dVayfBkg= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/gzip v0.0.3 h1:etUaeesHhEORpZMp18zoOhepboiWnFtXrBZxszWUn4k= @@ -208,6 +209,18 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-pay/crypto v0.0.1 h1:B6InT8CLfSLc6nGRVx9VMJRBBazFMjr293+jl0lLXUY= +github.com/go-pay/crypto v0.0.1/go.mod h1:41oEIvHMKbNcYlWUlRWtsnC6+ASgh7u29z0gJXe5bes= +github.com/go-pay/errgroup v0.0.2 h1:5mZMdm0TDClDm2S3G0/sm0f8AuQRtz0dOrTHDR9R8Cc= +github.com/go-pay/errgroup v0.0.2/go.mod h1:0+4b8mvFMS71MIzsaC+gVvB4x37I93lRb2dqrwuU8x8= +github.com/go-pay/gopay v1.5.103 h1:tjteCpcApf0CpiwKywMl6UnbLkMeYLJVEuVBtsQbyb8= +github.com/go-pay/gopay v1.5.103/go.mod h1:4+jKRvgmB8clKN1/E9M60miXKbnZ8wWGcg/Hsxn7k44= +github.com/go-pay/util v0.0.2 h1:goJ4f6kNY5zzdtg1Cj8oWC+Cw7bfg/qq2rJangMAb9U= +github.com/go-pay/util v0.0.2/go.mod h1:qM8VbyF1n7YAPZBSJONSPMPsPedhUTktewUAdf1AjPg= +github.com/go-pay/xlog v0.0.3 h1:avyMhCL/JgBHreoGx/am/kHxfs1udDOAeVqbmzP/Yes= +github.com/go-pay/xlog v0.0.3/go.mod h1:mH47xbobrdsSHWsmFtSF5agWbMHFP+tK0ZbVCk5OAEw= +github.com/go-pay/xtime v0.0.2 h1:7YR4/iuELsEHpJ6LUO0SVK80hQxDO9MLCfuVYIiTCRM= +github.com/go-pay/xtime v0.0.2/go.mod h1:W1yRbJaSt4CSBcdAtLBQ8xajiN/Pl5hquGczUcUE9xE= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= @@ -297,8 +310,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -793,8 +806,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5 golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -831,8 +844,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -885,8 +898,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -911,8 +924,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -987,8 +1000,8 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1000,8 +1013,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -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/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1071,8 +1084,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From c1e354e4abf58c45487526c55ba3bee98e9ecc6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E4=BF=8A=E5=AE=8F?= <389838709@qq.com> Date: Fri, 2 Aug 2024 13:47:52 +0800 Subject: [PATCH 02/14] =?UTF-8?q?=E6=94=AF=E4=BB=98=E8=B0=83=E8=B5=B7+?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/constants/common/common.go | 7 +- app/data/order.go | 11 -- .../controllers/front/payment_controller.go | 54 ++++++ app/http/entities/front/payment.go | 20 +++ app/http/requestmapping/front.go | 10 +- app/http/routes/route.go | 11 ++ app/mq/hanlers.go | 2 +- app/services/payment_service.go | 102 ----------- app/third/paymentService/ali_service.go | 149 +++++++++++++++ app/third/paymentService/payCommon/common.go | 28 +++ app/third/paymentService/payment_service.go | 130 ++++++++++++++ app/third/paymentService/payment_test.go | 139 ++++++++++++++ app/third/paymentService/wechat_service.go | 169 ++++++++++++++++++ app/utils/mq/mqmanager.go | 4 +- event/event.go | 2 +- 15 files changed, 715 insertions(+), 123 deletions(-) delete mode 100644 app/data/order.go create mode 100644 app/http/controllers/front/payment_controller.go create mode 100644 app/http/entities/front/payment.go delete mode 100644 app/services/payment_service.go create mode 100644 app/third/paymentService/ali_service.go create mode 100644 app/third/paymentService/payCommon/common.go create mode 100644 app/third/paymentService/payment_service.go create mode 100644 app/third/paymentService/payment_test.go create mode 100644 app/third/paymentService/wechat_service.go diff --git a/app/constants/common/common.go b/app/constants/common/common.go index 5ddaf65..08797ac 100644 --- a/app/constants/common/common.go +++ b/app/constants/common/common.go @@ -1,9 +1,10 @@ package common const ( - TOKEN_PRE = "player_token_" - TOKEN_Admin = "Admin_token_" - ADMIN_V1 = "/admin/pay/api/v1" + TOKEN_PRE = "player_token_" + TOKEN_Admin = "Admin_token_" + ADMIN_V1 = "/admin/pay/api/v1" + FRONT_API_V1 = "/v1" // 支付渠道枚举,1微信JSAPI,2微信H5,3微信app,4微信Native,5微信小程序,6支付宝网页&移动应用,7支付宝小程序,8支付宝JSAPI PAY_CHANNEL_UNKNOWN = 0 diff --git a/app/data/order.go b/app/data/order.go deleted file mode 100644 index 188550a..0000000 --- a/app/data/order.go +++ /dev/null @@ -1,11 +0,0 @@ -package data - -import "PaymentCenter/app/models/ordersmodel" - -func GetOrderInfoById(id int64) (orderInfo ordersmodel.Orders, err error) { - _, err = ordersmodel.GetInstance().GetDb().Where("Id = ?", id).Get(&orderInfo) - if err != nil { - return ordersmodel.Orders{}, err - } - return orderInfo, nil -} diff --git a/app/http/controllers/front/payment_controller.go b/app/http/controllers/front/payment_controller.go new file mode 100644 index 0000000..0f550b2 --- /dev/null +++ b/app/http/controllers/front/payment_controller.go @@ -0,0 +1,54 @@ +package front + +import ( + "github.com/gin-gonic/gin" + "github.com/go-pay/gopay/alipay" + "github.com/go-pay/xlog" + "github.com/qit-team/snow-core/log/logger" + + "net/http" +) + +// WxCallback 微信支付回调 +//func WxCallback(c *gin.Context) { +// notifyReq, err := wechat.V3ParseNotify(c.Request) +// if err != nil { +// xlog.Error(err) +// return +// } +// +// paymentService.WxPayCallBack(notifyReq) +// +// // ====↓↓↓====异步通知应答====↓↓↓==== +// // 退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功,否则会重试。 +// // 注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。 +// +// // 此写法是 gin 框架返回微信的写法 +// c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"}) +//} + +// AliCallback 支付宝支付回调 +func AliCallback(c *gin.Context) { + notifyReq, err := alipay.ParseNotifyToBodyMap(c.Request) // c.Request 是 gin 框架的写法 + logger.Info(c, "AliCallback-回调数据", c.Request) + if err != nil { + xlog.Error(err) + return + } + appId := notifyReq.Get("app_id") + if appId == "" { + c.String(http.StatusBadRequest, "%s", "fail") + } + + // todo 查询支付宝公钥证书信息,进行验证签名 + alipayPublicCert := "" + ok, err := alipay.VerifySignWithCert([]byte(alipayPublicCert), notifyReq) + if !ok { + logger.Error(c, "AliCallback-回调数据,验签失败") + c.String(http.StatusBadRequest, "%s", "fail") + } + + // todo 拼装数据触发下游回调,数据待定 + + c.String(http.StatusOK, "%s", "success") +} diff --git a/app/http/entities/front/payment.go b/app/http/entities/front/payment.go new file mode 100644 index 0000000..a1a57de --- /dev/null +++ b/app/http/entities/front/payment.go @@ -0,0 +1,20 @@ +package front + +type WxRequest struct { + TradeNo string `json:"tradeNo" validate:"required"` // 交易流水号 + VoucherId string `json:"voucherId"` // 制码批次号 + VoucherCode string `json:"voucherCode"` // 串码 + CnclSt string `json:"cnclSt"` // 核销状态,4-已核销 + RedeemResult string `json:"redeemResult"` // 核销结果,00-成功,09- 失败 + MrchntNo string `json:"mrchntNo"` // 商户号 + Sign string `json:"sign"` +} + +type AliRequest struct { + MerchantId int `form:"merchantId" validate:"required"` + OutTradeNo string `form:"outTradeNo" validate:"required"` + Status string `form:"status" validate:"required"` + RechargeAccount string `form:"rechargeAccount"` + CardCode string `form:"cardCode"` + Sign string `form:"sign" validate:"required"` +} diff --git a/app/http/requestmapping/front.go b/app/http/requestmapping/front.go index 36ccad7..a4a71ac 100644 --- a/app/http/requestmapping/front.go +++ b/app/http/requestmapping/front.go @@ -1,7 +1,11 @@ package requestmapping +import ( + "PaymentCenter/app/constants/common" + "PaymentCenter/app/http/entities/front" +) + var FrontRequestMap = map[string]func() interface{}{ - //"/v1/login": func() interface{} { - // return new(front.LoginRequest) - //}, + common.FRONT_API_V1 + "/notify/wx": func() interface{} { return new(front.WxRequest) }, + common.FRONT_API_V1 + "/notify/ali": func() interface{} { return new(front.AliRequest) }, } diff --git a/app/http/routes/route.go b/app/http/routes/route.go index 56425b3..cee46c4 100644 --- a/app/http/routes/route.go +++ b/app/http/routes/route.go @@ -5,6 +5,7 @@ package routes */ import ( "PaymentCenter/app/http/controllers" + "PaymentCenter/app/http/controllers/front" "PaymentCenter/app/http/middlewares" "PaymentCenter/app/http/trace" "PaymentCenter/app/utils/metric" @@ -49,6 +50,16 @@ func RegisterRoute(router *gin.Engine) { // //} + v1 := router.Group("/v1", middlewares.ValidateRequest()) + { + //回调处理 + notify := v1.Group("/notify") + { + notify.POST("/wx", front.WxCallback) + notify.POST("/ali", front.AliCallback) + } + } + router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) //router.GET("/hello", controllers.HelloHandler) diff --git a/app/mq/hanlers.go b/app/mq/hanlers.go index 9990bac..01510ac 100644 --- a/app/mq/hanlers.go +++ b/app/mq/hanlers.go @@ -14,7 +14,7 @@ func SingleTalk(tag uint64, ch interface{}, msg []byte) { // } // } // if !sendOk { - // common.PikaTool.Zadd(utils.GetRealKey(common2.USER_MSG)+data.Msg.To, data, time.Now().Unix()) + // payCommon.PikaTool.Zadd(utils.GetRealKey(common2.USER_MSG)+data.Msg.To, data, time.Now().Unix()) // } //} diff --git a/app/services/payment_service.go b/app/services/payment_service.go deleted file mode 100644 index cd1c95c..0000000 --- a/app/services/payment_service.go +++ /dev/null @@ -1,102 +0,0 @@ -package services - -import ( - "PaymentCenter/app/constants/common" - "PaymentCenter/app/constants/errorcode" - "PaymentCenter/app/data" - "context" - "errors" - "github.com/go-pay/gopay" - "github.com/go-pay/gopay/wechat/v3" - "github.com/go-pay/xlog" - "time" -) - -func ActivityGoodsCreateService(orderId int64) (err error) { - // 获取订单信息 - orderInfo, err := data.GetOrderInfoById(orderId) - if err != nil { - return err - } - if orderInfo.PayId == 0 { - return errors.New("缺少订单信息,无法进行支付") - } - // 获取支付渠道 - payChannelInfo, err := data.GetPayChannelById(orderInfo.PayId) - if err != nil { - return err - } - - switch payChannelInfo.ChannelType { - case common.PAYCHANNEL_WX_H5: - //todo 微信H5支付 - wxH5PayInfo() - break - case common.PAYCHANNEL_ALI_H5: - //todo 支付宝H5支付 - aLiH5PayInfo() - break - default: - return errors.New("暂不支持该支付渠道,请后续再使用") - } - return nil -} - -func wxH5PayInfo(c context.Context) { - // NewClientV3 初始化微信客户端 v3 - // mchid:商户ID 或者服务商模式的 sp_mchid - // serialNo:商户证书的证书序列号 - // apiV3Key:apiV3Key,商户平台获取 - // privateKey:私钥 apiclient_key.pem 读取后的内容 - client, err := wechat.NewClientV3(MchId, SerialNo, APIv3Key, PrivateKey) - if err != nil { - return - } - - // 设置微信平台API证书和序列号(推荐开启自动验签,无需手动设置证书公钥等信息) - //client.SetPlatformCert([]byte(""), "") - - // 启用自动同步返回验签,并定时更新微信平台API证书(开启自动验签时,无需单独设置微信平台API证书和序列号) - err = client.AutoVerifySign() - if err != nil { - return - } - - // 自定义配置http请求接收返回结果body大小,默认 10MB - client.SetBodySize(10) // 没有特殊需求,可忽略此配置 - - // 打开Debug开关,输出日志,默认是关闭的 - client.DebugSwitch = gopay.DebugOn - expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) - // 初始化 BodyMap - bm := make(gopay.BodyMap) - bm.Set("sp_appid", "sp_appid"). - Set("sp_mchid", "sp_mchid"). - Set("sub_mchid", "sub_mchid"). - Set("description", "测试Jsapi支付商品"). - Set("out_trade_no", tradeNo). - Set("time_expire", expire). - Set("notify_url", "https://www.fmm.ink"). - SetBodyMap("amount", func(bm gopay.BodyMap) { - bm.Set("total", 1). - Set("currency", "CNY") - }). - SetBodyMap("payer", func(bm gopay.BodyMap) { - bm.Set("sp_openid", "asdas") - }) - - wxRsp, err := client.V3TransactionH5(c, bm) - if err != nil { - xlog.Error(err) - return - } - if wxRsp.Code == errorcode.Success { - xlog.Debugf("wxRsp: %#v", wxRsp.Response) - return - } - xlog.Errorf("wxRsp:%s", wxRsp.Error) -} - -func aLiH5PayInfo() { - -} diff --git a/app/third/paymentService/ali_service.go b/app/third/paymentService/ali_service.go new file mode 100644 index 0000000..95bf674 --- /dev/null +++ b/app/third/paymentService/ali_service.go @@ -0,0 +1,149 @@ +package paymentService + +import ( + "PaymentCenter/app/third/paymentService/payCommon" + "context" + "errors" + "fmt" + "github.com/go-pay/gopay" + "github.com/go-pay/gopay/alipay" + "strconv" + "sync" +) + +var ( + aliClient *alipay.Client + aliClientErr error + aliOnce sync.Once +) + +// AliInitClient 使用提供的支付请求参数初始化支付宝客户端 +func AliInitClient(aliConfig AliPay) { + aliOnce.Do(func() { + // 初始化支付宝客户端 + // appid:应用ID + // privateKey:应用私钥,支持PKCS1和PKCS8 + // isProd:是否是正式环境,沙箱环境请选择新版沙箱应用。 + aliClient, aliClientErr = alipay.NewClient(aliConfig.AppId, aliConfig.PrivateKey, true) + }) + // 自定义配置http请求接收返回结果body大小,默认 10MB + aliClient.SetBodySize(10) // 没有特殊需求,可忽略此配置 + + // 打开Debug开关,输出日志,默认关闭 + aliClient.DebugSwitch = gopay.DebugOn + + // 设置支付宝请求 公共参数 + // 注意:具体设置哪些参数,根据不同的方法而不同,此处列举出所有设置参数 + aliClient.SetLocation(alipay.LocationShanghai). // 设置时区,不设置或出错均为默认服务器时间 + SetCharset(alipay.UTF8). // 设置字符编码,不设置默认 utf-8 + SetSignType(alipay.RSA2) // 设置签名类型,不设置默认 RSA2 + + // 自动同步验签(只支持证书模式) + // 传入 alipayPublicCert.crt 内容 + aliClient.AutoVerifySign(aliConfig.AlipayPublicCert) + + // 证书内容 + aliClientErr = aliClient.SetCertSnByContent(aliConfig.AppPublicCert, aliConfig.AlipayRootCert, aliConfig.AlipayPublicCert) +} + +// GetAliClient 获取已经初始化的支付宝客户端 +func GetAliClient() (*alipay.Client, error) { + if aliClient == nil { + return nil, errors.New("client not initialized") + } + if aliClientErr != nil { + return nil, aliClientErr + } + return aliClient, nil +} + +// ALiH5PayInfo 支付宝手机网站支付 +func ALiH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, error) { + // 初始化支付宝客户端 + AliInitClient(payOrderRequest.Ali) + + // 获取支付宝客户端 + aliClient, err := GetAliClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return "", err + } + // 初始化 BodyMap + amount := float64(payOrderRequest.Amount) / 100.0 + + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", payOrderRequest.OrderId). + Set("total_amount", amount). + Set("subject", payOrderRequest.Description). + Set("notify_url", payCommon.ALI_NOTIFY_URL) + + aliRsp, err := aliClient.TradeWapPay(c, bm) + if err != nil { + if bizErr, ok := alipay.IsBizError(err); ok { + return "", bizErr + } + return "", err + } + return aliRsp, nil +} + +// ALiOrderQuery 查询支付宝订单 +func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOrderQueryInfo, error) { + // 初始化支付宝客户端 + AliInitClient(aliConfig) + + // 获取支付宝客户端 + aliClient, err := GetAliClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return PayOrderQueryInfo{}, err + } + // 请求参数 + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", OrderNo) + + // 查询订单 + aliRsp, err := aliClient.TradeQuery(ctx, bm) + if err != nil { + if bizErr, ok := alipay.IsBizError(err); ok { + return PayOrderQueryInfo{}, bizErr + } + return PayOrderQueryInfo{}, err + } + // 同步返回验签 + ok, err := alipay.VerifySyncSignWithCert(aliConfig.AlipayPublicCert, aliRsp.SignData, aliRsp.Sign) + if err != nil || !ok { + return PayOrderQueryInfo{}, errors.New(fmt.Sprintf("验签失败,失败原因:%s", err.Error())) + } + + // 映射交易状态 + tradeState := "" + tradeStateDesc := "" + switch aliRsp.Response.TradeStatus { + case "TRADE_SUCCESS": + tradeState = "SUCCESS" + tradeStateDesc = "交易支付成功" + case "REFUND": + tradeState = "REFUND" + case "WAIT_BUYER_PAY": + tradeState = "NOTPAY" + tradeStateDesc = "交易创建,等待买家付款" + case "TRADE_CLOSED": + tradeState = "CLOSED" + tradeStateDesc = "未付款交易超时关闭,或支付完成后全额退款" + } + + amountTotal, _ := strconv.Atoi(aliRsp.Response.TotalAmount) + payerTotal, _ := strconv.Atoi(aliRsp.Response.BuyerPayAmount) + // 构建数据 + return PayOrderQueryInfo{ + AppId: aliConfig.AppId, + OutTradeNo: aliRsp.Response.OutTradeNo, + TransactionId: aliRsp.Response.TradeNo, + TradeState: tradeState, + TradeStateDesc: tradeStateDesc, + SuccessTime: aliRsp.Response.SendPayDate, + AmountTotal: int64(amountTotal * 100), + PayerTotal: int64(payerTotal * 100), + }, nil +} diff --git a/app/third/paymentService/payCommon/common.go b/app/third/paymentService/payCommon/common.go new file mode 100644 index 0000000..4fad793 --- /dev/null +++ b/app/third/paymentService/payCommon/common.go @@ -0,0 +1,28 @@ +package payCommon + +const ( + // 支付渠道枚举,1微信JSAPI,2微信H5,3微信app,4微信Native,5微信小程序,6支付宝网页&移动应用,7支付宝小程序,8支付宝JSAPI + PAY_CHANNEL_UNKNOWN = 0 + PAY_CHANNEL_WECHAT_JSAPI = 1 + PAY_CHANNEL_WECHAT_H5 = 2 + PAY_CHANNEL_WECHAT_APP = 3 + PAY_CHANNEL_WECHAT_NATIVE = 4 + PAY_CHANNEL_WECHAT_MINI = 5 + PAY_CHANNEL_ALIPAY_WEB = 6 + PAY_CHANNEL_ALIPAY_MINI = 7 + PAY_CHANNEL_ALIPAY_JSAPI = 8 + + PAY_SUCCESS_CODE = 200 // 支付响应成功 + PAY_ERROR_CODE = 500 // 支付响应失败 + PAY_NOT_FOUND_CODE = 404 // 未找到支付渠道 + + WX_SUCCESS_CODE = 200 // 微信状态码返回成功 + WX_NOTIFY_URL = "" // 微信支付回调地址 + + ALI_NOTIFY_URL = "" // 支付宝支付回调地址 + + ORDER_NO_TYPE_ORDER_NO = 2 + + PAY_CHANNLE_TYPE_WECHAT = 1 // 支付类型: 微信 + PAY_CHANNLE_TYPE_ZFB = 2 // 支付类型:支付宝 +) diff --git a/app/third/paymentService/payment_service.go b/app/third/paymentService/payment_service.go new file mode 100644 index 0000000..b2b38b3 --- /dev/null +++ b/app/third/paymentService/payment_service.go @@ -0,0 +1,130 @@ +package paymentService + +import ( + "PaymentCenter/app/third/paymentService/payCommon" + "context" + "strconv" +) + +type PayOrderRequest struct { + OrderId int64 `json:"order_id"` // 平台订单号 + ChannelType int `json:"channel_type"` // 支付方式 + Description string `json:"description"` // 商品描述 + Amount int `json:"amount"` // 金额单位为分 + PayerClientIp string `json:"payer_client_ip"` // 终端IP + Wx WxPay `json:"wx"` + Ali AliPay `json:"ali"` +} + +type WxPay struct { + AppId string `json:"app_id"` // 应用ID + MchId string `json:"mch_id"` // 商户ID 或者服务商模式的 sp_mchid + SerialNo string `json:"serial_no"` // 商户证书的证书序列号 + ApiV3Key string `json:"api_v_3_key"` // apiV3Key,商户平台获取 + PrivateKey string `json:"private_key"` // 私钥 apiclient_key.pem 读取后的内容 +} + +type AliPay struct { + AppId string `json:"app_id"` // 应用ID + PrivateKey string `json:"private_key"` // 应用私钥 + AppPublicCert []byte `json:"app_public_cert"` // 应用公钥 + AlipayRootCert []byte `json:"alipay_root_cert"` // 支付宝根证书 + AlipayPublicCert []byte `json:"alipay_public_cert"` // 支付宝公钥 +} + +type PayOrderResponse struct { + Code int `json:"code"` + ErrorMsg string `json:"error_msg"` + Result string `json:"result"` +} + +// PaymentService 统一发起支付 +func PaymentService(c context.Context, payOrderRequest PayOrderRequest) (payOrderResponse PayOrderResponse) { + switch payOrderRequest.ChannelType { + case payCommon.PAY_CHANNEL_WECHAT_H5: + // 微信H5支付 + info, err := WxH5PayInfo(c, payOrderRequest) + if err != nil { + return PayOrderResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: err.Error(), + } + } + return PayOrderResponse{ + Code: payCommon.PAY_SUCCESS_CODE, + ErrorMsg: "", + Result: info, + } + case payCommon.PAY_CHANNEL_ALIPAY_WEB: + // 支付宝H5支付 + info, err := ALiH5PayInfo(c, payOrderRequest) + if err != nil { + return PayOrderResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: err.Error(), + } + } + return PayOrderResponse{ + Code: payCommon.PAY_SUCCESS_CODE, + ErrorMsg: "", + Result: info, + } + default: + return PayOrderResponse{ + Code: payCommon.PAY_NOT_FOUND_CODE, + ErrorMsg: "暂不支持该支付渠道,请后续再使用", + } + } +} + +type PayOrderQueryRequest struct { + OrderId int64 `json:"order_id"` // 商户订单号 + PayChannel int `json:"pay_channel"` // 支付渠道类型 1:wx,2:zfb + Wx WxPay `json:"wx"` + Ali AliPay `json:"ali"` +} + +type PayOrderQueryResponse struct { + Code int `json:"code"` + ErrorMsg string `json:"error_msg"` + Result PayOrderQueryInfo `json:"result"` +} + +type PayOrderQueryInfo struct { + AppId string `json:"app_id"` // 应用ID + OutTradeNo string `json:"out_trade_no"` // 商家订单号 + TransactionId string `json:"transaction_id"` // 第三方订单号 + TradeState string `json:"trade_state"` // 交易状态 SUCCESS:成功、REFUND:转入退款、NOTPAY:未支付、CLOSED:已关闭 + TradeStateDesc string `json:"trade_state_desc"` // 交易描述 + SuccessTime string `json:"success_time"` // 交易完成时间 + AmountTotal int64 `json:"amount_total"` // 订单总金额,单位:分 + PayerTotal int64 `json:"payer_total"` // 支付总金额,单位:分 +} + +// PayOrderQuery 查询订单状态 +func PayOrderQuery(c context.Context, payOrderQueryRequest PayOrderQueryRequest) PayOrderQueryResponse { + var err error + var info PayOrderQueryInfo + switch payOrderQueryRequest.PayChannel { + case payCommon.PAY_CHANNLE_TYPE_WECHAT: + // 微信H5支付 + info, err = WxOrderQuery(c, payOrderQueryRequest.Wx, strconv.FormatInt(payOrderQueryRequest.OrderId, 10)) + case payCommon.PAY_CHANNLE_TYPE_ZFB: + info, err = ALiOrderQuery(c, payOrderQueryRequest.Ali, strconv.FormatInt(payOrderQueryRequest.OrderId, 10)) + default: + return PayOrderQueryResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: "暂不支持该支付渠道,请后续再使用", + } + } + if err != nil { + return PayOrderQueryResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: err.Error(), + } + } + return PayOrderQueryResponse{ + Code: payCommon.PAY_SUCCESS_CODE, + Result: info, + } +} diff --git a/app/third/paymentService/payment_test.go b/app/third/paymentService/payment_test.go new file mode 100644 index 0000000..7f5ea24 --- /dev/null +++ b/app/third/paymentService/payment_test.go @@ -0,0 +1,139 @@ +package paymentService + +import ( + "context" + "crypto/md5" + "encoding/hex" + "encoding/pem" + "errors" + "github.com/go-pay/gopay" + "github.com/tjfoc/gmsm/x509" + "os" + "strings" + "testing" +) + +func TestMarketSendRequest_Market(t *testing.T) { + c := context.Background() + //appCertPath := "appCertPublicKey_2021003140624267.crt" // 应用公钥证书 + //aliPayPublicCertPath := "alipayCertPublicKey_RSA2.crt" // 支付宝公钥证书 + //aliPayRootCertPath := "alipayRootCert.crt" // 支付宝根证书 + //sn, err := GetCertSN(appCertPath) + //getCertSN, err := GetCertSN(aliPayPublicCertPath) + //if err != nil { + // return + //} + //certSN, err := GetRootCertSN(aliPayRootCertPath) + //if err != nil { + // return + //} + //if err != nil { + // return + //} + + request := PayOrderRequest{ + OrderId: 12312312312, + ChannelType: 6, + Description: "测试商品", + AppId: "2021003140624267", + Amount: 1, + PayerClientIp: "192.168.110.235", + Ali: AliPay{ + PrivateKey: "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCYNADnzJNuCO4k2Amly9NrDr59zwkJJ91ugEl5FXKIKFkSxbH2JPfzz4iBAVNw3LxjDbaFXYHWl7/lDwaqZLrw/kMYdEM5/0WQcQkmIVP34Ch25EPhW4j55R/llNM2CECIzhJA2s8HbQmX6ZQPemJ58f5D7JhAHr5wPvGvFDKgsgWxp146vvAP09PjmYyqokSWSVGvtZxJMB0X3wXfoGYoPWEBxlWNuVdRhnreHLb19h+BzsOf/fiqOU0nJdJUlaYtKGdrmkKWmzs5eOZPpjjTggwap1mpOqAiKySUPVWod1pHjzelpU4XJglhtqojgaRvhKR4UptpZlzo3DddKRWZAgMBAAECggEAWZPJsKMDZfBVkFOhofWLEWgVDuBJthzDQuS32U6ZIK92dQjrSy3IzLKo6RwJqtQO/9EzSdyVrOGU68/jIWe1al6KyJqscjbeDzunocxdqfZAenCvzrNbs495LVpg0F0c9icqMQdWb01qJwEuxclGqfSVrA5UMPCnLlKtVVhyz4XuRvgGfNtKEb064bSP4x0v2IFJfptXyJfU+jQBFsKw+jpp1l+mioCgrfuo2jky9O4aZmuRxMRfljdHP1IVI8JXSbiLoSgWyS2RazzH4I1QZRDu9wXX/GVkW4wVQvgXj1fM4wRy3aY6rgHsmvWkCRw2pR5ALVgHbrqWvZefg+K8AQKBgQDLPr9SwWL3rAuTPFJSPgBMkNUELcGzidHwKMdWTVm7fBNJfljP9iBaL4tega1J76DPXZv9Ed1AYwLMbAYJy+HDh2z2v6hejKYLfbCE3njM/JNantjdUdRaPQCdANxtR5fNGeWg5jmoxVykkxEuI2OBV6H2VHF2Uu+pH9dttudXgQKBgQC/tZ0wPpNqzNNnyUiqH1fRvZEfAIltassxYyEWs0wQfIAwIcC7W7/qDAecC+1AQ2GJUsIEN+XERHxm3mlW4ZO4uhv2F8RBH2L678UYMVcmLIdu09Dhs6cgOSz+RE05pZ44iwtcZMN49Jj/oA8UsgsyRt0W/vSzBG/oztLh7maKGQKBgBTE17Y0LtHSUeh8zIi87zjjv/DKYDXhioN5F02ZX+cJucrTz22I4Ct3Gk3RAI1CoHMwRKVqsupGc93B2CgeHnblmtvoH+NxifI4qNYbCcsgsEodW1naZ9HE1BA3K087dqat08pKXHADowDqSmIlmaB4/QL4BBQsS3ND3rbGbwOBAoGABCmeWyDQtJ3z7Sn6sO7zSjOShhjN4xlRhiX/3u1Ptn/tjJ3/AFJyrVNLiwDbSuzrnj60xcCs9YEDLnyfzuog+f7YgpRrh3p1/LreDbNskAChhC+HNo9fDp0PNAopnxnQgKvMOFpbwDGXbN191lYzVgkh3dD1YTm0cFqlS5CQm2kCgYBbXqL/XUr+TUVcQTU+MF2/j6i7kaRKfgpu3/PPBBHK+FsCrv6dFgqH4W3aW+ea/PyyUMX3e0DkdwDefHNqvNGpkde6E4s0L1Oigied88PTgEwvOnKLJKpskztFXxov21C3O8BQrj/p2L5aCek/g5GXbBco9M46Y7zrrPzYX2GgUw==", + AppPublicCert: []byte("-----BEGIN CERTIFICATE-----\nMIIEhTCCA22gAwIBAgIQICQIATSiK0uukHhZeagASDANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE\nBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0\naG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs\nYXNzIDEgUjEwHhcNMjQwODAxMDgxOTQ1WhcNMjkwNzMxMDgxOTQ1WjBNMQswCQYDVQQGEwJDTjES\nMBAGA1UECgwJ6ZmI5L+K5a6PMQ8wDQYDVQQLDAZBbGlwYXkxGTAXBgNVBAMMEDIwODg2MDIzMjc1\nMjQyMjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYNADnzJNuCO4k2Amly9NrDr59\nzwkJJ91ugEl5FXKIKFkSxbH2JPfzz4iBAVNw3LxjDbaFXYHWl7/lDwaqZLrw/kMYdEM5/0WQcQkm\nIVP34Ch25EPhW4j55R/llNM2CECIzhJA2s8HbQmX6ZQPemJ58f5D7JhAHr5wPvGvFDKgsgWxp146\nvvAP09PjmYyqokSWSVGvtZxJMB0X3wXfoGYoPWEBxlWNuVdRhnreHLb19h+BzsOf/fiqOU0nJdJU\nlaYtKGdrmkKWmzs5eOZPpjjTggwap1mpOqAiKySUPVWod1pHjzelpU4XJglhtqojgaRvhKR4Uptp\nZlzo3DddKRWZAgMBAAGjggEpMIIBJTAfBgNVHSMEGDAWgBRxB+IEYRbk5fJl6zEPyeD0PJrVkTAd\nBgNVHQ4EFgQUyzUfsFBHwX1WrVU34DqmiPWcJrkwQAYDVR0gBDkwNzA1BgdggRwBbgEBMCowKAYI\nKwYBBQUHAgEWHGh0dHA6Ly9jYS5hbGlwYXkuY29tL2Nwcy5wZGYwDgYDVR0PAQH/BAQDAgbAMC8G\nA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9jYS5hbGlwYXkuY29tL2NybDkyLmNybDBgBggrBgEFBQcB\nAQRUMFIwKAYIKwYBBQUHMAKGHGh0dHA6Ly9jYS5hbGlwYXkuY29tL2NhNi5jZXIwJgYIKwYBBQUH\nMAGGGmh0dHA6Ly9jYS5hbGlwYXkuY29tOjgzNDAvMA0GCSqGSIb3DQEBCwUAA4IBAQA5k9ffbPz6\nMrHFGdMrmjLjAKNaRCVLnfkk+i2AvmqGouoXKPTnM+dT6i4FeXWsW+c3VfLFND7Wpi6kgEnj/Ngl\nIBciDaPto2nEPagHsAPlO3Kak4k4oXusn/SR1OY9AcWiCvWpmJVOhmescQlugy8c0FK2zSjJhRHb\nMEVD9jAvcxE+NVYC3ePXZvWD0t+95TQuLIID6qprBsr/FouzRQuYYirKkx9YVS9yFhOsSyhYRbwr\naxbBFhKEX4C2UntpuTfwf99Sw7jvhJF2CfyxloRxt/s0WZg4TVNl983ne8FK3igIq9i/gl7A2vnq\n+IDA+t0ojOpKKB3P8DutsdEQeEAt\n-----END CERTIFICATE-----"), + AlipayRootCert: []byte("-----BEGIN CERTIFICATE-----\nMIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG\nEwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw\nMzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO\nUkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE\nMPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT\nV7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti\nW/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ\nMxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b\n53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI\npDoiVhsLwg==\n-----END CERTIFICATE-----\n\n-----BEGIN CERTIFICATE-----\nMIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE\nBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj\nYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj\nYXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0\nMFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV\nBAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j\naWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF\nAAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk\nrUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2\nxAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp\ndRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6\nvSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl\nYUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1\nPbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H\nDtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98\nSZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG\nPsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe\n9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC\nAwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90\ntATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy\nnOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf\ntJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq\nJSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3\nIODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW\n05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41\nT0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI\nkkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop\nPKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N\n1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y\njXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02\n77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi\nkT9qhqn+lw==\n-----END CERTIFICATE-----\n\n-----BEGIN CERTIFICATE-----\nMIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG\nEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh\ndGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh\ndGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0\nWjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE\nCwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp\nYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA\nIgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU\nWP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt\nrpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ\n4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2\nzVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg\nwHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH\nRglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA==\n-----END CERTIFICATE-----\n\n-----BEGIN CERTIFICATE-----\nMIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF\nBQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM\nE0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg\nMiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq\nMQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp\nbmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv\nb3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV\nnJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5\n4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg\nwykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw\nWktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN\nz+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g\nKgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA\nuTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF\nMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp\nemMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3\nU8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I\nUugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn\nDJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU\n1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX\nYf4Zr0fJsGuv\n-----END CERTIFICATE-----"), + AlipayPublicCert: []byte("-----BEGIN CERTIFICATE-----\nMIIDljCCAn6gAwIBAgIQICQIAVaBz6Cq6s3snHwxkTANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE\nBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0\naG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs\nYXNzIDIgUjEwHhcNMjQwODAxMDgxOTQ1WhcNMjkwNzMxMDgxOTQ1WjB3MQswCQYDVQQGEwJDTjES\nMBAGA1UECgwJ6ZmI5L+K5a6PMQ8wDQYDVQQLDAZBbGlwYXkxQzBBBgNVBAMMOuaUr+S7mOWunSjk\nuK3lm70p572R57uc5oqA5pyv5pyJ6ZmQ5YWs5Y+4LTIwODg2MDIzMjc1MjQyMjMwggEiMA0GCSqG\nSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6sezAFwgc1ZdcjhiKgzwJVkx22JFF1WeOneBi7ySk4GX6\n+fNT/dAnIq7RLgUzbo/rgIlZVQiybKHCPiTnqboovAQjunFERilKKkjlBNVEF6FmMkEFVz0J5OkJ\nkgrPL+qUkPckrT1iHKhKpxifhOJgJk2j/xd3aPt8sAiy/ks2nv4JLPItVmQhFI6Fm7wPzG/qsuS/\n+zQpQRDp5ahiDBLkoMReV7k13o3WTW6pJaFMRVPvQhVs+IlbaAb9zgOfQrstYD6at3CeKa70/XDy\nIOt20hGtluUmgPSlSw0hDMAvlaZXgIEDcHfnbl1uNXgox3IoZ2BAkXh+ZDpxANHFotvfAgMBAAGj\nEjAQMA4GA1UdDwEB/wQEAwID+DANBgkqhkiG9w0BAQsFAAOCAQEAhw6OpEeHLUGFUW4x0/kiULM7\n/z8pzu6yDuk+V6buhQErPBmo6E2cAfw5YAAPADvanNVV7V/qV5PG3IgI5MsblH6pLf4cusOUYBRP\nqTJu7BXsGiXiLfBtTPBP/dBIuStru4pg31pNX+QCseed9X26gd8oPKEjC2Zsk7eSPqPzhC9uUeNc\n2yUTHzsgXJNU8pZj37XvKlKlmyGP0xnzghQINND9ckj3a2blBwnj4yX8uH0xUzu+KM/LSt9U8+B+\nfychPXzLWST+zmVGB1QrdXETD5wWCJOCFCTefwmkdgLZxIOYVqxNVGk7/Mbs6zfkR4vXtXC7aOJX\nvU3sP3ZTuuL+ww==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIE4jCCAsqgAwIBAgIIYsSr5bKAMl8wDQYJKoZIhvcNAQELBQAwejELMAkGA1UEBhMCQ04xFjAU\nBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEw\nLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMy\nMjE0MzQxNVoXDTM3MTEyNjE0MzQxNVowgYIxCzAJBgNVBAYTAkNOMRYwFAYDVQQKDA1BbnQgRmlu\nYW5jaWFsMSAwHgYDVQQLDBdDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTE5MDcGA1UEAwwwQW50IEZp\nbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBDbGFzcyAyIFIxMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAsLMfYaoRoPRbmDcAfXPCmKf43pWRN5yTXa/KJWO0l+mrgQvs89bA\nNEvbDUxlkGwycwtwi5DgBuBgVhLliXu+R9CYgr2dXs8D8Hx/gsggDcyGPLmVrDOnL+dyeauheARZ\nfA3du60fwEwwbGcVIpIxPa/4n3IS/ElxQa6DNgqxh8J9Xwh7qMGl0JK9+bALuxf7B541Gr4p0WEN\nG8fhgjBV4w4ut9eQLOoa1eddOUSZcy46Z7allwowwgt7b5VFfx/P1iKJ3LzBMgkCK7GZ2kiLrL7R\niqV+h482J7hkJD+ardoc6LnrHO/hIZymDxok+VH9fVeUdQa29IZKrIDVj65THQIDAQABo2MwYTAf\nBgNVHSMEGDAWgBRfdLQEwE8HWurlsdsio4dBspzhATAdBgNVHQ4EFgQUSqHkYINtUSAtDPnS8Xoy\noP9p7qEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIB\nAIQ8TzFy4bVIVb8+WhHKCkKNPcJe2EZuIcqvRoi727lZTJOfYy/JzLtckyZYfEI8J0lasZ29wkTt\na1IjSo+a6XdhudU4ONVBrL70U8Kzntplw/6TBNbLFpp7taRALjUgbCOk4EoBMbeCL0GiYYsTS0mw\n7xdySzmGQku4GTyqutIGPQwKxSj9iSFw1FCZqr4VP4tyXzMUgc52SzagA6i7AyLedd3tbS6lnR5B\nL+W9Kx9hwT8L7WANAxQzv/jGldeuSLN8bsTxlOYlsdjmIGu/C9OWblPYGpjQQIRyvs4Cc/mNhrh+\n14EQgwuemIIFDLOgcD+iISoN8CqegelNcJndFw1PDN6LkVoiHz9p7jzsge8RKay/QW6C03KNDpWZ\nEUCgCUdfHfo8xKeR+LL1cfn24HKJmZt8L/aeRZwZ1jwePXFRVtiXELvgJuM/tJDIFj2KD337iV64\nfWcKQ/ydDVGqfDZAdcU4hQdsrPWENwPTQPfVPq2NNLMyIH9+WKx9Ed6/WzeZmIy5ZWpX1TtTolo6\nOJXQFeItMAjHxW/ZSZTok5IS3FuRhExturaInnzjYpx50a6kS34c5+c8hYq7sAtZ/CNLZmBnBCFD\naMQqT8xFZJ5uolUaSeXxg7JFY1QsYp5RKvj4SjFwCGKJ2+hPPe9UyyltxOidNtxjaknOCeBHytOr\n-----END CERTIFICATE-----\n"), + }, + } + PaymentService(c, request) +} + +func GetCertSN(certPathOrData any) (sn string, err error) { + var certData []byte + switch pathOrData := certPathOrData.(type) { + case string: + certData, err = os.ReadFile(pathOrData) + if err != nil { + return gopay.NULL, err + } + case []byte: + certData = pathOrData + default: + return gopay.NULL, errors.New("certPathOrData 证书类型断言错误") + } + + if block, _ := pem.Decode(certData); block != nil { + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return gopay.NULL, err + } + name := cert.Issuer.String() + serialNumber := cert.SerialNumber.String() + h := md5.New() + h.Write([]byte(name)) + h.Write([]byte(serialNumber)) + sn = hex.EncodeToString(h.Sum(nil)) + } + if sn == gopay.NULL { + return gopay.NULL, errors.New("failed to get sn,please check your cert") + } + return sn, nil +} + +func GetRootCertSN(rootCertPathOrData any) (sn string, err error) { + var ( + certData []byte + certEnd = `-----END CERTIFICATE-----` + ) + switch pathOrData := rootCertPathOrData.(type) { + case string: + certData, err = os.ReadFile(pathOrData) + if err != nil { + return gopay.NULL, err + } + case []byte: + certData = pathOrData + default: + return gopay.NULL, errors.New("rootCertPathOrData 断言异常") + } + + pems := strings.Split(string(certData), certEnd) + for _, c := range pems { + if block, _ := pem.Decode([]byte(c + certEnd)); block != nil { + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + continue + } + if !allowSignatureAlgorithm[cert.SignatureAlgorithm.String()] { + continue + } + name := cert.Issuer.String() + serialNumber := cert.SerialNumber.String() + h := md5.New() + h.Write([]byte(name)) + h.Write([]byte(serialNumber)) + if sn == gopay.NULL { + sn += hex.EncodeToString(h.Sum(nil)) + } else { + sn += "_" + hex.EncodeToString(h.Sum(nil)) + } + } + } + if sn == gopay.NULL { + return gopay.NULL, errors.New("failed to get sn,please check your cert") + } + return sn, nil +} + +// 允许进行 sn 提取的证书签名算法 +var allowSignatureAlgorithm = map[string]bool{ + "MD2-RSA": true, + "MD5-RSA": true, + "SHA1-RSA": true, + "SHA256-RSA": true, + "SHA384-RSA": true, + "SHA512-RSA": true, + "SHA256-RSAPSS": true, + "SHA384-RSAPSS": true, + "SHA512-RSAPSS": true, +} diff --git a/app/third/paymentService/wechat_service.go b/app/third/paymentService/wechat_service.go new file mode 100644 index 0000000..3ad2c14 --- /dev/null +++ b/app/third/paymentService/wechat_service.go @@ -0,0 +1,169 @@ +package paymentService + +import ( + "PaymentCenter/app/third/paymentService/payCommon" + "context" + "errors" + "fmt" + "github.com/go-pay/gopay" + "github.com/go-pay/gopay/wechat/v3" + "sync" + "time" +) + +var ( + wxClient *wechat.ClientV3 + clientErr error + once sync.Once +) + +// InitClient 使用提供的支付请求参数初始化微信客户端 +func InitClient(wxConfig WxPay) { + once.Do(func() { + // NewClientV3 初始化微信客户端 v3 + // mchid:商户ID 或者服务商模式的 sp_mchid + // serialNo:商户证书的证书序列号 + // apiV3Key:apiV3Key,商户平台获取 + // privateKey:私钥 apiclient_key.pem 读取后的内容 + wxClient, clientErr = wechat.NewClientV3( + wxConfig.MchId, + wxConfig.SerialNo, + wxConfig.ApiV3Key, + wxConfig.PrivateKey, + ) + }) + // 启用自动同步返回验签,并定时更新微信平台API证书(开启自动验签时,无需单独设置微信平台API证书和序列号) + clientErr = wxClient.AutoVerifySign() + + // 自定义配置http请求接收返回结果body大小,默认 10MB + wxClient.SetBodySize(10) // 没有特殊需求,可忽略此配置 + + // 打开Debug开关,输出日志,默认是关闭的 + wxClient.DebugSwitch = gopay.DebugOn +} + +// GetClient 获取已经初始化的微信客户端 +func GetClient() (*wechat.ClientV3, error) { + if wxClient == nil { + return nil, errors.New("client not initialized") + } + if clientErr != nil { + return nil, clientErr + } + return wxClient, nil +} + +// WxH5PayInfo 微信H5支付 +func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, error) { + // 初始化微信客户端 + InitClient(payOrderRequest.Wx) + + // 获取微信客户端 + wxClient, err := GetClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return "", err + } + expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) + // 初始化 BodyMap + bm := make(gopay.BodyMap) + bm.Set("appid", payOrderRequest.Wx.AppId). + Set("mchid", payOrderRequest.Wx.MchId). + Set("description", payOrderRequest.Description). + Set("out_trade_no", payOrderRequest.OrderId). + Set("time_expire", expire). + Set("notify_url", payCommon.WX_NOTIFY_URL). + SetBodyMap("amount", func(bm gopay.BodyMap) { + bm.Set("total", payOrderRequest.Amount). + Set("currency", "CNY") + }). + SetBodyMap("scene_info", func(bm gopay.BodyMap) { + bm.Set("payer_client_ip", payOrderRequest.PayerClientIp). + SetBodyMap("h5_info", func(bm gopay.BodyMap) { + bm.Set("type", "common") + }) + }) + + wxRsp, err := wxClient.V3TransactionH5(c, bm) + if err != nil { + return "", err + } + if wxRsp.Code != wechat.Success || wxRsp.Response.H5Url == "" { + return "", errors.New(fmt.Sprintf("发起支付失败,失败状态码:%d, 失败原因:%s", wxRsp.Code, wxRsp.Error)) + } + return wxRsp.Response.H5Url, nil +} + +//func WxPayCallBack(notifyReq *wechat.V3NotifyReq) error { +// // 获取附加数据中的信息 +// appId := notifyReq.Resource.AssociatedData +// +// payOrderRequest := PayOrderRequest{} +// // 初始化微信客户端 +// InitClient(payOrderRequest) +// +// // 获取微信客户端 +// wxClient, err := GetClient() +// if err != nil { +// fmt.Println("Failed to get client:", err) +// return err +// } +// +// // 获取微信平台证书 +// certMap := wxClient.WxPublicKeyMap() +// // 验证异步通知的签名 +// err = notifyReq.VerifySignByPKMap(certMap) +// if err != nil { +// return err +// } +// objPtr := "" +// // 通用通知解密(推荐此方法) +// result := notifyReq.DecryptCipherTextToStruct(apiV3Key, objPtr) +//} + +// WxOrderQuery 查询微信订单 +func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrderQueryInfo, error) { + // 初始化微信客户端 + InitClient(wxConfig) + + // 获取微信客户端 + wxClient, err := GetClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return PayOrderQueryInfo{}, err + } + + wxRsp, err := wxClient.V3TransactionQueryOrder(ctx, payCommon.ORDER_NO_TYPE_ORDER_NO, orderNo) + if err != nil { + return PayOrderQueryInfo{}, err + } + if wxRsp.Code != wechat.Success { + return PayOrderQueryInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error)) + } + + // 映射交易状态 + tradeState := "" + switch wxRsp.Response.TradeState { + case "SUCCESS": + tradeState = "SUCCESS" + case "REFUND": + tradeState = "REFUND" + case "NOTPAY": + tradeState = "NOTPAY" + case "CLOSED": + tradeState = "CLOSED" + } + amountTotal := wxRsp.Response.Amount.Total + payerTotal := wxRsp.Response.Amount.PayerTotal + + return PayOrderQueryInfo{ + AppId: wxRsp.Response.Appid, + OutTradeNo: wxRsp.Response.OutTradeNo, + TransactionId: wxRsp.Response.TransactionId, + TradeState: tradeState, + TradeStateDesc: wxRsp.Response.TradeStateDesc, + SuccessTime: wxRsp.Response.SuccessTime, + AmountTotal: int64(amountTotal), + PayerTotal: int64(payerTotal), + }, nil +} diff --git a/app/utils/mq/mqmanager.go b/app/utils/mq/mqmanager.go index 8ac56b2..270cd69 100644 --- a/app/utils/mq/mqmanager.go +++ b/app/utils/mq/mqmanager.go @@ -23,8 +23,8 @@ type CMqManager struct { func (this *CMqManager) InitMq() { this.mqs = make(map[string]Imq) - //this.mqs[common.MQ_RABBIT] = RabbitMq{} - //this.mqs[common.MQ_NSQ] = NsqMq{} + //this.mqs[payCommon.MQ_RABBIT] = RabbitMq{} + //this.mqs[payCommon.MQ_NSQ] = NsqMq{} this.mqs[common3.MQ_NATS] = mq.NatsMq{} this.mqs[common3.MQ_KFK] = mq.KafkaMq{} } diff --git a/event/event.go b/event/event.go index a81ca03..5c17fc0 100644 --- a/event/event.go +++ b/event/event.go @@ -7,7 +7,7 @@ import ( /** *事件触发 - event.EventManger.TrigerEvent(common.Event_USER_LOG_IN, param) + event.EventManger.TrigerEvent(payCommon.Event_USER_LOG_IN, param) */ var EventManger *EventManagerFactory From 4d4338f17830c259e01a9e840f59998e032d7775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E4=BF=8A=E5=AE=8F?= <389838709@qq.com> Date: Fri, 2 Aug 2024 13:49:28 +0800 Subject: [PATCH 03/14] =?UTF-8?q?=E6=94=AF=E4=BB=98=E8=B0=83=E8=B5=B7+?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/third/paymentService/payment_test.go | 2 +- app/utils/mq/mqmanager.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/third/paymentService/payment_test.go b/app/third/paymentService/payment_test.go index 7f5ea24..0e39b97 100644 --- a/app/third/paymentService/payment_test.go +++ b/app/third/paymentService/payment_test.go @@ -35,10 +35,10 @@ func TestMarketSendRequest_Market(t *testing.T) { OrderId: 12312312312, ChannelType: 6, Description: "测试商品", - AppId: "2021003140624267", Amount: 1, PayerClientIp: "192.168.110.235", Ali: AliPay{ + AppId: "2021003140624267", PrivateKey: "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCYNADnzJNuCO4k2Amly9NrDr59zwkJJ91ugEl5FXKIKFkSxbH2JPfzz4iBAVNw3LxjDbaFXYHWl7/lDwaqZLrw/kMYdEM5/0WQcQkmIVP34Ch25EPhW4j55R/llNM2CECIzhJA2s8HbQmX6ZQPemJ58f5D7JhAHr5wPvGvFDKgsgWxp146vvAP09PjmYyqokSWSVGvtZxJMB0X3wXfoGYoPWEBxlWNuVdRhnreHLb19h+BzsOf/fiqOU0nJdJUlaYtKGdrmkKWmzs5eOZPpjjTggwap1mpOqAiKySUPVWod1pHjzelpU4XJglhtqojgaRvhKR4UptpZlzo3DddKRWZAgMBAAECggEAWZPJsKMDZfBVkFOhofWLEWgVDuBJthzDQuS32U6ZIK92dQjrSy3IzLKo6RwJqtQO/9EzSdyVrOGU68/jIWe1al6KyJqscjbeDzunocxdqfZAenCvzrNbs495LVpg0F0c9icqMQdWb01qJwEuxclGqfSVrA5UMPCnLlKtVVhyz4XuRvgGfNtKEb064bSP4x0v2IFJfptXyJfU+jQBFsKw+jpp1l+mioCgrfuo2jky9O4aZmuRxMRfljdHP1IVI8JXSbiLoSgWyS2RazzH4I1QZRDu9wXX/GVkW4wVQvgXj1fM4wRy3aY6rgHsmvWkCRw2pR5ALVgHbrqWvZefg+K8AQKBgQDLPr9SwWL3rAuTPFJSPgBMkNUELcGzidHwKMdWTVm7fBNJfljP9iBaL4tega1J76DPXZv9Ed1AYwLMbAYJy+HDh2z2v6hejKYLfbCE3njM/JNantjdUdRaPQCdANxtR5fNGeWg5jmoxVykkxEuI2OBV6H2VHF2Uu+pH9dttudXgQKBgQC/tZ0wPpNqzNNnyUiqH1fRvZEfAIltassxYyEWs0wQfIAwIcC7W7/qDAecC+1AQ2GJUsIEN+XERHxm3mlW4ZO4uhv2F8RBH2L678UYMVcmLIdu09Dhs6cgOSz+RE05pZ44iwtcZMN49Jj/oA8UsgsyRt0W/vSzBG/oztLh7maKGQKBgBTE17Y0LtHSUeh8zIi87zjjv/DKYDXhioN5F02ZX+cJucrTz22I4Ct3Gk3RAI1CoHMwRKVqsupGc93B2CgeHnblmtvoH+NxifI4qNYbCcsgsEodW1naZ9HE1BA3K087dqat08pKXHADowDqSmIlmaB4/QL4BBQsS3ND3rbGbwOBAoGABCmeWyDQtJ3z7Sn6sO7zSjOShhjN4xlRhiX/3u1Ptn/tjJ3/AFJyrVNLiwDbSuzrnj60xcCs9YEDLnyfzuog+f7YgpRrh3p1/LreDbNskAChhC+HNo9fDp0PNAopnxnQgKvMOFpbwDGXbN191lYzVgkh3dD1YTm0cFqlS5CQm2kCgYBbXqL/XUr+TUVcQTU+MF2/j6i7kaRKfgpu3/PPBBHK+FsCrv6dFgqH4W3aW+ea/PyyUMX3e0DkdwDefHNqvNGpkde6E4s0L1Oigied88PTgEwvOnKLJKpskztFXxov21C3O8BQrj/p2L5aCek/g5GXbBco9M46Y7zrrPzYX2GgUw==", AppPublicCert: []byte("-----BEGIN CERTIFICATE-----\nMIIEhTCCA22gAwIBAgIQICQIATSiK0uukHhZeagASDANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE\nBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0\naG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs\nYXNzIDEgUjEwHhcNMjQwODAxMDgxOTQ1WhcNMjkwNzMxMDgxOTQ1WjBNMQswCQYDVQQGEwJDTjES\nMBAGA1UECgwJ6ZmI5L+K5a6PMQ8wDQYDVQQLDAZBbGlwYXkxGTAXBgNVBAMMEDIwODg2MDIzMjc1\nMjQyMjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYNADnzJNuCO4k2Amly9NrDr59\nzwkJJ91ugEl5FXKIKFkSxbH2JPfzz4iBAVNw3LxjDbaFXYHWl7/lDwaqZLrw/kMYdEM5/0WQcQkm\nIVP34Ch25EPhW4j55R/llNM2CECIzhJA2s8HbQmX6ZQPemJ58f5D7JhAHr5wPvGvFDKgsgWxp146\nvvAP09PjmYyqokSWSVGvtZxJMB0X3wXfoGYoPWEBxlWNuVdRhnreHLb19h+BzsOf/fiqOU0nJdJU\nlaYtKGdrmkKWmzs5eOZPpjjTggwap1mpOqAiKySUPVWod1pHjzelpU4XJglhtqojgaRvhKR4Uptp\nZlzo3DddKRWZAgMBAAGjggEpMIIBJTAfBgNVHSMEGDAWgBRxB+IEYRbk5fJl6zEPyeD0PJrVkTAd\nBgNVHQ4EFgQUyzUfsFBHwX1WrVU34DqmiPWcJrkwQAYDVR0gBDkwNzA1BgdggRwBbgEBMCowKAYI\nKwYBBQUHAgEWHGh0dHA6Ly9jYS5hbGlwYXkuY29tL2Nwcy5wZGYwDgYDVR0PAQH/BAQDAgbAMC8G\nA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9jYS5hbGlwYXkuY29tL2NybDkyLmNybDBgBggrBgEFBQcB\nAQRUMFIwKAYIKwYBBQUHMAKGHGh0dHA6Ly9jYS5hbGlwYXkuY29tL2NhNi5jZXIwJgYIKwYBBQUH\nMAGGGmh0dHA6Ly9jYS5hbGlwYXkuY29tOjgzNDAvMA0GCSqGSIb3DQEBCwUAA4IBAQA5k9ffbPz6\nMrHFGdMrmjLjAKNaRCVLnfkk+i2AvmqGouoXKPTnM+dT6i4FeXWsW+c3VfLFND7Wpi6kgEnj/Ngl\nIBciDaPto2nEPagHsAPlO3Kak4k4oXusn/SR1OY9AcWiCvWpmJVOhmescQlugy8c0FK2zSjJhRHb\nMEVD9jAvcxE+NVYC3ePXZvWD0t+95TQuLIID6qprBsr/FouzRQuYYirKkx9YVS9yFhOsSyhYRbwr\naxbBFhKEX4C2UntpuTfwf99Sw7jvhJF2CfyxloRxt/s0WZg4TVNl983ne8FK3igIq9i/gl7A2vnq\n+IDA+t0ojOpKKB3P8DutsdEQeEAt\n-----END CERTIFICATE-----"), AlipayRootCert: []byte("-----BEGIN CERTIFICATE-----\nMIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG\nEwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw\nMzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO\nUkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE\nMPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT\nV7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti\nW/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ\nMxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b\n53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI\npDoiVhsLwg==\n-----END CERTIFICATE-----\n\n-----BEGIN CERTIFICATE-----\nMIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE\nBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj\nYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj\nYXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0\nMFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV\nBAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j\naWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF\nAAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk\nrUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2\nxAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp\ndRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6\nvSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl\nYUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1\nPbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H\nDtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98\nSZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG\nPsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe\n9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC\nAwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90\ntATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy\nnOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf\ntJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq\nJSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3\nIODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW\n05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41\nT0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI\nkkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop\nPKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N\n1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y\njXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02\n77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi\nkT9qhqn+lw==\n-----END CERTIFICATE-----\n\n-----BEGIN CERTIFICATE-----\nMIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG\nEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh\ndGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh\ndGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0\nWjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE\nCwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp\nYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA\nIgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU\nWP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt\nrpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ\n4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2\nzVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg\nwHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH\nRglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA==\n-----END CERTIFICATE-----\n\n-----BEGIN CERTIFICATE-----\nMIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF\nBQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM\nE0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg\nMiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq\nMQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp\nbmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv\nb3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV\nnJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5\n4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg\nwykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw\nWktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN\nz+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g\nKgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA\nuTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF\nMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp\nemMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3\nU8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I\nUugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn\nDJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU\n1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX\nYf4Zr0fJsGuv\n-----END CERTIFICATE-----"), diff --git a/app/utils/mq/mqmanager.go b/app/utils/mq/mqmanager.go index 270cd69..8ac56b2 100644 --- a/app/utils/mq/mqmanager.go +++ b/app/utils/mq/mqmanager.go @@ -23,8 +23,8 @@ type CMqManager struct { func (this *CMqManager) InitMq() { this.mqs = make(map[string]Imq) - //this.mqs[payCommon.MQ_RABBIT] = RabbitMq{} - //this.mqs[payCommon.MQ_NSQ] = NsqMq{} + //this.mqs[common.MQ_RABBIT] = RabbitMq{} + //this.mqs[common.MQ_NSQ] = NsqMq{} this.mqs[common3.MQ_NATS] = mq.NatsMq{} this.mqs[common3.MQ_KFK] = mq.KafkaMq{} } From f8f0ac29137d9860cbb0f81ec8fe373d1dcc08a9 Mon Sep 17 00:00:00 2001 From: wolter Date: Fri, 2 Aug 2024 13:54:25 +0800 Subject: [PATCH 04/14] =?UTF-8?q?=E5=BA=94=E7=94=A8=E5=92=8C=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E6=96=B9=E5=BC=8F=E4=BF=AE=E6=94=B9=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=95=86=E6=88=B7id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/http/entities/backend/app.go | 14 +++++------ app/http/entities/backend/pay_channel.go | 10 +++++--- app/services/app.go | 32 ++++++++++-------------- app/services/pay_channel.go | 14 +---------- 4 files changed, 27 insertions(+), 43 deletions(-) diff --git a/app/http/entities/backend/app.go b/app/http/entities/backend/app.go index c35bb55..dc4233b 100644 --- a/app/http/entities/backend/app.go +++ b/app/http/entities/backend/app.go @@ -50,7 +50,7 @@ type AppCreateRequest struct { PrivateKey string `json:"private_key" validate:"required" label:"应用私钥"` MerchantPublicKey string `json:"merchant_public_key" label:"商户公钥"` WhiteIp string `json:"white_ip"` - NotifyUrl string `json:"notify_url"` + NotifyUrl string `json:"notify_url" validate:"required" label:"通知地址"` } func (a *AppCreateRequest) RequestToDb() (db appmodel.App) { @@ -69,13 +69,12 @@ func (a *AppCreateRequest) RequestToDb() (db appmodel.App) { type AppUpdateRequest struct { Id int64 `json:"id" validate:"required" label:"应用ID"` - MerchantId int64 `json:"merchant_id" validate:"required" label:"商户ID"` - AppName string `json:"app_name" validate:"required" label:"应用名称"` + AppName string `json:"app_name" label:"应用名称"` AppRemark string `json:"app_remark" label:"应用备注"` - Status int `json:"status" validate:"oneof=0 1 2" label:"应用状态"` - KeyType int `json:"key_type" validate:"required" label:"应用密钥类型"` - PublicKey string `json:"public_key" validate:"required" label:"应用公钥"` - PrivateKey string `json:"private_key" validate:"required" label:"应用私钥"` + Status int `json:"status" label:"应用状态"` + KeyType int `json:"key_type" label:"应用密钥类型"` + PublicKey string `json:"public_key" label:"应用公钥"` + PrivateKey string `json:"private_key" label:"应用私钥"` MerchantPublicKey string `json:"merchant_public_key" label:"商户公钥"` WhiteIp string `json:"white_ip"` NotifyUrl string `json:"notify_url"` @@ -83,7 +82,6 @@ type AppUpdateRequest struct { func (a *AppUpdateRequest) RequestToDb() (db appmodel.App) { db.Id = a.Id - db.MerchantId = a.MerchantId db.AppName = a.AppName db.AppRemark = a.AppRemark db.Status = a.Status diff --git a/app/http/entities/backend/pay_channel.go b/app/http/entities/backend/pay_channel.go index 7bbb096..c2260ac 100644 --- a/app/http/entities/backend/pay_channel.go +++ b/app/http/entities/backend/pay_channel.go @@ -91,14 +91,18 @@ type PayChannelListRequest struct { } type PayChannelUpdateRequest struct { - Id int64 `json:"id" validate:"required"` - PayChannelCreateRequest + Id int64 `json:"id" validate:"required"` + PayName string `json:"pay_name" validate:"required" label:"支付渠道名称"` + ChannelType int `json:"channel_type" validate:"required" label:"支付渠道"` //支付渠道枚举,1微信JSAPI,2微信H5,3微信app,4微信Native,5微信小程序,6支付宝网页&移动应用,7支付宝小程序,8支付宝JSAPI + AppId string `json:"app_id" validate:"required" label:"应用appId"` + ExpireTime string `json:"expire_time"` + AliPayPayChannel AliPayPayChannel `json:"ali_pay_pay_channel,omitempty"` + WechatPayChannel WechatPayChannel `json:"wechat_pay_channel,omitempty"` } func (p PayChannelUpdateRequest) RequestToDb() (db paychannelmodel.PayChannel, err error) { db.Id = p.Id db.PayName = p.PayName - db.MerchantId = p.MerchantId db.ChannelType = p.ChannelType db.AppId = p.AppId if p.ExpireTime != "" { diff --git a/app/services/app.go b/app/services/app.go index 90dd740..7777b82 100644 --- a/app/services/app.go +++ b/app/services/app.go @@ -26,13 +26,13 @@ func AppList(req backend.AppListRequest) (result []appmodel.App, total int64, co return appList, count, code } -func AppCreate(App *appmodel.App) (code int) { +func AppCreate(app *appmodel.App) (code int) { db := paychannelmodel.GetInstance().GetDb() repo := data.NewAppRepo(db) merchantRepo := data.NewMerchantRepo(db) // 拼接查询条件 conn := builder.NewCond() - conn = conn.And(builder.Eq{"id": App.MerchantId}) + conn = conn.And(builder.Eq{"id": app.MerchantId}) merchant := merchantmodel.Merchant{} has, err := merchantRepo.MerchantDetail(&merchant, conn) if err != nil { @@ -42,31 +42,25 @@ func AppCreate(App *appmodel.App) (code int) { return errorcode.MerchantNotFound } - _, err = repo.AppInsertOne(App) + _, err = repo.AppInsertOne(app) code = handErr(err) return } -func AppUpdate(App *appmodel.App) (code int) { +func AppUpdate(app *appmodel.App) (code int) { + var err error db := paychannelmodel.GetInstance().GetDb() repo := data.NewAppRepo(db) - merchantRepo := data.NewMerchantRepo(db) - // 拼接查询条件 - conn := builder.NewCond() - conn = conn.And(builder.Eq{"id": App.MerchantId}) - merchant := merchantmodel.Merchant{} - has, err := merchantRepo.MerchantDetail(&merchant, conn) - if err != nil { - return handErr(err) - } - if !has { - return errorcode.MerchantNotFound - } // 拼接查询条件 - uconn := builder.NewCond() - uconn = uconn.And(builder.Eq{"Id": App.Id}) - _, err = repo.AppUpdate(App, uconn, "app_remark") + conn := builder.NewCond() + conn = conn.And(builder.Eq{"Id": app.Id}) + if app.AppName != "" { + // 编辑页面更新,备注和白名单IP可更新为空 + _, err = repo.AppUpdate(app, conn, "app_remark", "white_ip") + } else { + _, err = repo.AppUpdate(app, conn) + } code = handErr(err) return diff --git a/app/services/pay_channel.go b/app/services/pay_channel.go index 0e19da3..3d3b57b 100644 --- a/app/services/pay_channel.go +++ b/app/services/pay_channel.go @@ -49,23 +49,11 @@ func PayChannelCreate(payChannel *paychannelmodel.PayChannel) (code int) { func PayChannelUpdate(payChannel *paychannelmodel.PayChannel) (code int) { db := paychannelmodel.GetInstance().GetDb() repo := data.NewPayChannelRepo(db) - merchantRepo := data.NewMerchantRepo(db) - // 拼接查询条件 - conn := builder.NewCond() - conn = conn.And(builder.Eq{"id": payChannel.MerchantId}) - merchant := merchantmodel.Merchant{} - has, err := merchantRepo.MerchantDetail(&merchant, conn) - if err != nil { - return handErr(err) - } - if !has { - return errorcode.MerchantNotFound - } // 拼接查询条件 uconn := builder.NewCond() uconn = uconn.And(builder.Eq{"Id": payChannel.Id}) - _, err = repo.PayChannelUpdate(payChannel, uconn, "white_ip") + _, err := repo.PayChannelUpdate(payChannel, uconn, "white_ip") code = handErr(err) return From 92c1a3c230f4b473d786aba66dd4fae0f35a86d6 Mon Sep 17 00:00:00 2001 From: wolter Date: Fri, 2 Aug 2024 14:02:13 +0800 Subject: [PATCH 05/14] =?UTF-8?q?=E6=B7=BB=E5=8A=A0dockerfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..264e9ac --- /dev/null +++ b/Dockerfile @@ -0,0 +1,43 @@ + +## 使用官方Go镜像作为构建环境 +#FROM golang:1.21.0 AS builder +# +## 设置工作目录 +#WORKDIR /app +# +## 复制项目源码 +#COPY . . +# +## 复制go模块依赖文件 +#COPY go.mod go.sum ./ +# +## 安装go模块依赖 +#RUN go env -w GOPROXY=https://goproxy.cn,direct +#RUN go mod tidy +# +## 编译Go应用程序,生成静态链接的二进制文件 +#RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server . + +# 创建最终镜像,用于运行编译后的Go程序 +FROM alpine + +RUN echo 'http://mirrors.ustc.edu.cn/alpine/v3.5/main' > /etc/apk/repositories \ +&& echo 'http://mirrors.ustc.edu.cn/alpine/v3.5/community' >>/etc/apk/repositories \ +&& apk update && apk add tzdata \ +&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ +&& echo "Asia/Shanghai" > /etc/timezone \ + +ENV server = "admin" + +# 设置工作目录 +WORKDIR /app + +# 将编译好的二进制文件从构建阶段复制到运行阶段 +COPY /server . +#COPY --from=builder /app/server . +COPY .env . + + +ENV TZ=Asia/Shanghai +# 设置容器启动时运行的命令 +ENTRYPOINT ["/app/server", "-a", "$server"] \ No newline at end of file From bf4e24619d23124e55a73e6487efda71489412de Mon Sep 17 00:00:00 2001 From: wolter Date: Fri, 2 Aug 2024 14:32:44 +0800 Subject: [PATCH 06/14] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E8=B7=AF=E7=94=B1=EF=BC=8C=E6=B7=BB=E5=8A=A0auth=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/constants/common/common.go | 7 ++++- app/http/middlewares/base.go | 52 +++++++++++++++++++++++----------- app/http/routes/admin.go | 2 +- app/utils/util.go | 10 +++++++ config/config.go | 1 + 5 files changed, 54 insertions(+), 18 deletions(-) diff --git a/app/constants/common/common.go b/app/constants/common/common.go index 5ddaf65..d763935 100644 --- a/app/constants/common/common.go +++ b/app/constants/common/common.go @@ -3,7 +3,7 @@ package common const ( TOKEN_PRE = "player_token_" TOKEN_Admin = "Admin_token_" - ADMIN_V1 = "/admin/pay/api/v1" + ADMIN_V1 = "/pay/admin/api/v1" // 支付渠道枚举,1微信JSAPI,2微信H5,3微信app,4微信Native,5微信小程序,6支付宝网页&移动应用,7支付宝小程序,8支付宝JSAPI PAY_CHANNEL_UNKNOWN = 0 @@ -15,4 +15,9 @@ const ( PAY_CHANNEL_ALIPAY_WEB = 6 PAY_CHANNEL_ALIPAY_MINI = 7 PAY_CHANNEL_ALIPAY_JSAPI = 8 + + // 统一登陆信息 + ADMIN_USER_ID = "User-Id" + ADMIN_USER_NAME = "User-Name" + ADMIN_USER_INCLUDEUSERS = "Include-Users" ) diff --git a/app/http/middlewares/base.go b/app/http/middlewares/base.go index c13dedd..728350d 100644 --- a/app/http/middlewares/base.go +++ b/app/http/middlewares/base.go @@ -6,10 +6,8 @@ import ( "PaymentCenter/app/http/controllers" "PaymentCenter/app/http/requestmapping" "PaymentCenter/app/utils" - "context" - "errors" + "PaymentCenter/config" "github.com/gin-gonic/gin" - "github.com/qit-team/snow-core/redis" "strings" ) @@ -59,20 +57,42 @@ func Cors() gin.HandlerFunc { func AdminAuth() gin.HandlerFunc { return func(c *gin.Context) { - var token = c.GetHeader("token") - //将token放入redis - var playerId, err = redis.GetRedis().Get(context.Background(), utils.GetRealKey(common.TOKEN_Admin+token)).Result() - if rs, errRedis := redis.GetRedis().SIsMember(context.Background(), "disabled_uids", playerId).Result(); errRedis == nil && rs { - err = errors.New(errorcode.GetMsg(errorcode.NotFound, "")) - redis.GetRedis().SRem(context.Background(), "disabled_uids", playerId) - } - if err == nil { - c.Set("playerId", playerId) - c.Next() - return - } else { - controllers.HandCodeRes(c, nil, errorcode.Forbidden) + ip, _ := c.RemoteIP() + utils.Log(c, "请求地址RemoteIP()", ip.String(), config.GetConf().AdminGate) + clientIp := c.ClientIP() + utils.Log(c, "请求地址clientIp", clientIp) + + if config.GetConf().Debug == false && !utils.SliceInStr(ip.String(), config.GetConf().AdminGate) { c.Abort() + controllers.HandCodeRes(c, nil, errorcode.Forbidden) + return + } + + var userName = c.GetHeader("User-Name") + if userName != "" { + c.Set(common.ADMIN_USER_NAME, userName) + } + var IncludeUsers = c.GetHeader("Include-Users") + if IncludeUsers != "" { + c.Set(common.ADMIN_USER_INCLUDEUSERS, IncludeUsers) + } + + var adminId = c.GetHeader("User-Id") + // 测试环境直接放行 + if config.GetConf().Debug == true { + c.Set(common.ADMIN_USER_ID, adminId) + c.Next() + } else { + utils.Log(c, "请求header信息", "adminId="+adminId, "IncludeUsers="+IncludeUsers) + // 正式环境校验 + if adminId != "" { + c.Set(common.ADMIN_USER_ID, adminId) + c.Next() + } else { + c.Abort() + controllers.HandCodeRes(c, nil, errorcode.NotAuth) + return + } } } } diff --git a/app/http/routes/admin.go b/app/http/routes/admin.go index 7b531f8..9d81e75 100644 --- a/app/http/routes/admin.go +++ b/app/http/routes/admin.go @@ -23,7 +23,7 @@ func RegisterAdminRoute(router *gin.Engine) { } } - v1 := router.Group("/admin/pay/api/v1", middlewares.ValidateRequest()) + v1 := router.Group("/pay/admin/api/v1", middlewares.AdminAuth(), middlewares.ValidateRequest()) { // 商户管理 merchant := v1.Group("/merchant") diff --git a/app/utils/util.go b/app/utils/util.go index 42ef269..0810033 100644 --- a/app/utils/util.go +++ b/app/utils/util.go @@ -410,3 +410,13 @@ func ParseToken(tokenString string) (*jwt.Token, *Claims, error) { }) return token, Claims, err } + +// 判断切片是否包含指定字符串 +func SliceInStr(s string, slice []string) bool { + for _, v := range slice { + if s == v { + return true + } + } + return false +} diff --git a/config/config.go b/config/config.go index 9fdff6f..4db996c 100644 --- a/config/config.go +++ b/config/config.go @@ -36,6 +36,7 @@ type Config struct { OpenApi OpenApi `toml:"OpenApi"` Jwt Jwt `toml:"Jwt"` AliOss AliOss `toml:"AliOss"` + AdminGate []string `toml:"AdminGate"` } type AliOss struct { From f1ee9b4196b6dfcb007edd2487fbfce8ef0cf2a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E4=BF=8A=E5=AE=8F?= <389838709@qq.com> Date: Fri, 2 Aug 2024 16:37:07 +0800 Subject: [PATCH 07/14] =?UTF-8?q?=E6=94=AF=E4=BB=98=E9=80=80=E6=AC=BE+?= =?UTF-8?q?=E9=80=80=E6=AC=BE=E5=8D=95=E6=9F=A5=E8=AF=A2+=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=85=B3=E9=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/front/payment_controller.go | 73 ++++--- app/http/entities/front/payment.go | 19 -- app/http/routes/route.go | 6 +- app/third/paymentService/ali_service.go | 124 ++++++++++- app/third/paymentService/payCommon/common.go | 14 +- app/third/paymentService/payment_service.go | 180 +++++++++++++--- app/third/paymentService/wechat_service.go | 198 +++++++++++++++--- app/utils/mq/mqmanager.go | 6 +- 8 files changed, 508 insertions(+), 112 deletions(-) diff --git a/app/http/controllers/front/payment_controller.go b/app/http/controllers/front/payment_controller.go index 0f550b2..f22a12f 100644 --- a/app/http/controllers/front/payment_controller.go +++ b/app/http/controllers/front/payment_controller.go @@ -1,8 +1,11 @@ package front import ( + "PaymentCenter/app/third/paymentService" "github.com/gin-gonic/gin" + "github.com/go-pay/gopay" "github.com/go-pay/gopay/alipay" + "github.com/go-pay/gopay/wechat/v3" "github.com/go-pay/xlog" "github.com/qit-team/snow-core/log/logger" @@ -10,45 +13,57 @@ import ( ) // WxCallback 微信支付回调 -//func WxCallback(c *gin.Context) { -// notifyReq, err := wechat.V3ParseNotify(c.Request) -// if err != nil { -// xlog.Error(err) -// return -// } -// -// paymentService.WxPayCallBack(notifyReq) -// -// // ====↓↓↓====异步通知应答====↓↓↓==== -// // 退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功,否则会重试。 -// // 注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。 -// -// // 此写法是 gin 框架返回微信的写法 -// c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"}) -//} +func WxCallback(c *gin.Context) { + appId := c.Param("appId") + logger.Info(c, "WxCallback-回调数据APPID", appId) + if appId == "" { + c.String(http.StatusBadRequest, "%s", "fail") + } + + // todo 查询应用下的支付配置 + + var wxConfig paymentService.WxPay + + logger.Info(c, "WxCallback-回调数据", c.Request) + notifyReq, err := wechat.V3ParseNotify(c.Request) + if err != nil { + logger.Error(c, "WxCallback-回调数据验签失败", err.Error()) + return + } + + err = paymentService.WxPayCallBack(notifyReq, wxConfig) + if err != nil { + logger.Error(c, "WxCallback-回调执行失败,失败原因:", err.Error()) + return + } + + // ====↓↓↓====异步通知应答====↓↓↓==== + // 退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功,否则会重试。 + // 注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。 + c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"}) +} // AliCallback 支付宝支付回调 func AliCallback(c *gin.Context) { + appId := c.Param("appId") + logger.Info(c, "AliCallback-回调数据APPID", appId) + if appId == "" { + c.String(http.StatusBadRequest, "%s", "fail") + } + // todo 查询应用下的支付配置 + notifyReq, err := alipay.ParseNotifyToBodyMap(c.Request) // c.Request 是 gin 框架的写法 logger.Info(c, "AliCallback-回调数据", c.Request) if err != nil { xlog.Error(err) return } - appId := notifyReq.Get("app_id") - if appId == "" { - c.String(http.StatusBadRequest, "%s", "fail") + var aliConfig paymentService.AliPay + err = paymentService.ALiCallBack(notifyReq, aliConfig) + if err != nil { + logger.Error(c, "AliCallback-回调执行失败,失败原因:", err.Error()) + return } - // todo 查询支付宝公钥证书信息,进行验证签名 - alipayPublicCert := "" - ok, err := alipay.VerifySignWithCert([]byte(alipayPublicCert), notifyReq) - if !ok { - logger.Error(c, "AliCallback-回调数据,验签失败") - c.String(http.StatusBadRequest, "%s", "fail") - } - - // todo 拼装数据触发下游回调,数据待定 - c.String(http.StatusOK, "%s", "success") } diff --git a/app/http/entities/front/payment.go b/app/http/entities/front/payment.go index a1a57de..9bad7d0 100644 --- a/app/http/entities/front/payment.go +++ b/app/http/entities/front/payment.go @@ -1,20 +1 @@ package front - -type WxRequest struct { - TradeNo string `json:"tradeNo" validate:"required"` // 交易流水号 - VoucherId string `json:"voucherId"` // 制码批次号 - VoucherCode string `json:"voucherCode"` // 串码 - CnclSt string `json:"cnclSt"` // 核销状态,4-已核销 - RedeemResult string `json:"redeemResult"` // 核销结果,00-成功,09- 失败 - MrchntNo string `json:"mrchntNo"` // 商户号 - Sign string `json:"sign"` -} - -type AliRequest struct { - MerchantId int `form:"merchantId" validate:"required"` - OutTradeNo string `form:"outTradeNo" validate:"required"` - Status string `form:"status" validate:"required"` - RechargeAccount string `form:"rechargeAccount"` - CardCode string `form:"cardCode"` - Sign string `form:"sign" validate:"required"` -} diff --git a/app/http/routes/route.go b/app/http/routes/route.go index cee46c4..a3fe510 100644 --- a/app/http/routes/route.go +++ b/app/http/routes/route.go @@ -50,13 +50,13 @@ func RegisterRoute(router *gin.Engine) { // //} - v1 := router.Group("/v1", middlewares.ValidateRequest()) + v1 := router.Group("/v1") { //回调处理 notify := v1.Group("/notify") { - notify.POST("/wx", front.WxCallback) - notify.POST("/ali", front.AliCallback) + notify.POST("/wx/:appId", front.WxCallback) + notify.POST("/ali/:appId", front.AliCallback) } } diff --git a/app/third/paymentService/ali_service.go b/app/third/paymentService/ali_service.go index 95bf674..0e85140 100644 --- a/app/third/paymentService/ali_service.go +++ b/app/third/paymentService/ali_service.go @@ -75,7 +75,8 @@ func ALiH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, e bm.Set("out_trade_no", payOrderRequest.OrderId). Set("total_amount", amount). Set("subject", payOrderRequest.Description). - Set("notify_url", payCommon.ALI_NOTIFY_URL) + //Set("notify_url", payCommon.ALI_NOTIFY_URL_TEST+payOrderRequest.Ali.AppId). + Set("notify_url", payCommon.ALI_NOTIFY_URL_PROD+payOrderRequest.Ali.AppId) aliRsp, err := aliClient.TradeWapPay(c, bm) if err != nil { @@ -87,6 +88,17 @@ func ALiH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, e return aliRsp, nil } +// ALiCallBack 支付宝支付回调 +func ALiCallBack(notifyReq gopay.BodyMap, aliConfig AliPay) error { + ok, err := alipay.VerifySignWithCert(aliConfig.AlipayPublicCert, notifyReq) + if !ok || err != nil { + return err + } + + // todo 拼装数据触发下游回调,数据待定 + return nil +} + // ALiOrderQuery 查询支付宝订单 func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOrderQueryInfo, error) { // 初始化支付宝客户端 @@ -136,9 +148,10 @@ func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOr amountTotal, _ := strconv.Atoi(aliRsp.Response.TotalAmount) payerTotal, _ := strconv.Atoi(aliRsp.Response.BuyerPayAmount) // 构建数据 + outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo) return PayOrderQueryInfo{ AppId: aliConfig.AppId, - OutTradeNo: aliRsp.Response.OutTradeNo, + OutTradeNo: int64(outTradeNo), TransactionId: aliRsp.Response.TradeNo, TradeState: tradeState, TradeStateDesc: tradeStateDesc, @@ -147,3 +160,110 @@ func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOr PayerTotal: int64(payerTotal * 100), }, nil } + +// AliRefundOrder 支付宝退款申请 +func AliRefundOrder(ctx context.Context, orderRefundRequest OrderRefundRequest) (OrderRefundInfo, error) { + // 初始化支付宝客户端 + AliInitClient(orderRefundRequest.Ali) + + // 获取支付宝客户端 + aliClient, err := GetAliClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return OrderRefundInfo{}, err + } + // 请求参数 + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", orderRefundRequest.OrderId). + Set("refund_amount", orderRefundRequest.RefundAmount). + Set("refund_reason", orderRefundRequest.RefundReason). + Set("out_request_no", orderRefundRequest.RefundOrderId) + + // 发起退款请求 + aliRsp, err := aliClient.TradeRefund(ctx, bm) + if err != nil { + if bizErr, ok := alipay.IsBizError(err); ok { + return OrderRefundInfo{}, bizErr + } + return OrderRefundInfo{}, err + } + + refundFee, _ := strconv.Atoi(aliRsp.Response.RefundFee) + outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo) + return OrderRefundInfo{ + OutTradeNo: int64(outTradeNo), + TransactionId: aliRsp.Response.TradeNo, + RefundFee: int64(refundFee * 100), + RefundOrderId: orderRefundRequest.RefundOrderId, + RefundStatus: payCommon.PAY_REFUND_STATU_SUCCESS, + }, nil +} + +// AliRefundOrderQuery 支付宝订单退款查询 +func AliRefundOrderQuery(ctx context.Context, orderRefundQueryRequest OrderRefundQueryRequest) (OrderRefundInfo, error) { + // 初始化支付宝客户端 + AliInitClient(orderRefundQueryRequest.Ali) + + // 获取支付宝客户端 + aliClient, err := GetAliClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return OrderRefundInfo{}, err + } + + // 请求参数 + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", orderRefundQueryRequest.OrderId). + Set("out_request_no", orderRefundQueryRequest.RefundOrderId). + Set("query_options", []string{"gmt_refund_pay"}) + + // 发起退款查询请求 + aliRsp, err := aliClient.TradeFastPayRefundQuery(ctx, bm) + if err != nil { + if bizErr, ok := alipay.IsBizError(err); ok { + return OrderRefundInfo{}, bizErr + } + return OrderRefundInfo{}, err + } + refundFee, _ := strconv.Atoi(aliRsp.Response.RefundAmount) + outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo) + refundOrderId, _ := strconv.Atoi(aliRsp.Response.OutRequestNo) + return OrderRefundInfo{ + OutTradeNo: int64(outTradeNo), + TransactionId: aliRsp.Response.TradeNo, + RefundFee: int64(refundFee * 100), + RefundOrderId: int64(refundOrderId), + RefundStatus: payCommon.PAY_REFUND_STATU_SUCCESS, + RefundSuccessTime: aliRsp.Response.GmtRefundPay, + }, nil +} + +// AliCloseOrder 支付宝订单关闭 +func AliCloseOrder(ctx context.Context, orderCloseRequest OrderCloseRequest) (OrderCloseInfo, error) { + // 初始化支付宝客户端 + AliInitClient(orderCloseRequest.Ali) + + // 获取支付宝客户端 + aliClient, err := GetAliClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return OrderCloseInfo{}, err + } + // 请求参数 + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", orderCloseRequest.OrderId) + + // 条码支付 + aliRsp, err := aliClient.TradeClose(ctx, bm) + if err != nil { + if bizErr, ok := alipay.IsBizError(err); ok { + return OrderCloseInfo{}, bizErr + } + return OrderCloseInfo{}, err + } + + outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo) + return OrderCloseInfo{ + OutTradeNo: int64(outTradeNo), + }, nil +} diff --git a/app/third/paymentService/payCommon/common.go b/app/third/paymentService/payCommon/common.go index 4fad793..7c376df 100644 --- a/app/third/paymentService/payCommon/common.go +++ b/app/third/paymentService/payCommon/common.go @@ -1,6 +1,9 @@ package payCommon const ( + TEST_URL = "" + PROD_URL = "" + // 支付渠道枚举,1微信JSAPI,2微信H5,3微信app,4微信Native,5微信小程序,6支付宝网页&移动应用,7支付宝小程序,8支付宝JSAPI PAY_CHANNEL_UNKNOWN = 0 PAY_CHANNEL_WECHAT_JSAPI = 1 @@ -17,12 +20,19 @@ const ( PAY_NOT_FOUND_CODE = 404 // 未找到支付渠道 WX_SUCCESS_CODE = 200 // 微信状态码返回成功 - WX_NOTIFY_URL = "" // 微信支付回调地址 - ALI_NOTIFY_URL = "" // 支付宝支付回调地址 + WX_NOTIFY_URL_TEST = TEST_URL + "/v1/notify/wx/appId" // 微信支付回调地址 + WX_NOTIFY_URL_PROD = PROD_URL + "/v1/notify/wx/appId" // 微信支付回调地址 + ALI_NOTIFY_URL_TEST = TEST_URL + "/v1/notify/ali/appId" // 支付宝支付回调地址 + ALI_NOTIFY_URL_PROD = PROD_URL + "/v1/notify/ali/appId" // 支付宝支付回调地址 ORDER_NO_TYPE_ORDER_NO = 2 PAY_CHANNLE_TYPE_WECHAT = 1 // 支付类型: 微信 PAY_CHANNLE_TYPE_ZFB = 2 // 支付类型:支付宝 + + PAY_REFUND_STATU_COMMON = 0 // 未申请 + PAY_REFUND_STATU_ING = 1 // 退款中 + PAY_REFUND_STATU_SUCCESS = 2 // 退款成功 + PAY_REFUND_STATU_FAIL = 3 // 退款失败 ) diff --git a/app/third/paymentService/payment_service.go b/app/third/paymentService/payment_service.go index b2b38b3..1c06d7a 100644 --- a/app/third/paymentService/payment_service.go +++ b/app/third/paymentService/payment_service.go @@ -40,41 +40,32 @@ type PayOrderResponse struct { // PaymentService 统一发起支付 func PaymentService(c context.Context, payOrderRequest PayOrderRequest) (payOrderResponse PayOrderResponse) { + var err error + var info string switch payOrderRequest.ChannelType { case payCommon.PAY_CHANNEL_WECHAT_H5: // 微信H5支付 - info, err := WxH5PayInfo(c, payOrderRequest) - if err != nil { - return PayOrderResponse{ - Code: payCommon.PAY_ERROR_CODE, - ErrorMsg: err.Error(), - } - } - return PayOrderResponse{ - Code: payCommon.PAY_SUCCESS_CODE, - ErrorMsg: "", - Result: info, - } + info, err = WxH5PayInfo(c, payOrderRequest) case payCommon.PAY_CHANNEL_ALIPAY_WEB: // 支付宝H5支付 - info, err := ALiH5PayInfo(c, payOrderRequest) - if err != nil { - return PayOrderResponse{ - Code: payCommon.PAY_ERROR_CODE, - ErrorMsg: err.Error(), - } - } - return PayOrderResponse{ - Code: payCommon.PAY_SUCCESS_CODE, - ErrorMsg: "", - Result: info, - } + info, err = ALiH5PayInfo(c, payOrderRequest) default: return PayOrderResponse{ Code: payCommon.PAY_NOT_FOUND_CODE, ErrorMsg: "暂不支持该支付渠道,请后续再使用", } } + if err != nil { + return PayOrderResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: err.Error(), + } + } + return PayOrderResponse{ + Code: payCommon.PAY_SUCCESS_CODE, + ErrorMsg: "", + Result: info, + } } type PayOrderQueryRequest struct { @@ -92,7 +83,7 @@ type PayOrderQueryResponse struct { type PayOrderQueryInfo struct { AppId string `json:"app_id"` // 应用ID - OutTradeNo string `json:"out_trade_no"` // 商家订单号 + OutTradeNo int64 `json:"out_trade_no"` // 商家订单号 TransactionId string `json:"transaction_id"` // 第三方订单号 TradeState string `json:"trade_state"` // 交易状态 SUCCESS:成功、REFUND:转入退款、NOTPAY:未支付、CLOSED:已关闭 TradeStateDesc string `json:"trade_state_desc"` // 交易描述 @@ -128,3 +119,142 @@ func PayOrderQuery(c context.Context, payOrderQueryRequest PayOrderQueryRequest) Result: info, } } + +type OrderRefundRequest struct { + OrderId int64 `json:"order_id"` // 订单编号 + RefundOrderId int64 `json:"refund_order_id"` // 退款订单号 + RefundReason string `json:"refund_reason"` // 退款原因 + RefundAmount int64 `json:"refund_amount"` // 退款金额,单位分 + PayChannel int `json:"pay_channel"` // 支付渠道类型 1:wx,2:zfb + Wx WxPay `json:"wx"` + Ali AliPay `json:"ali"` +} + +type OrderRefundResponse struct { + Code int `json:"code"` + ErrorMsg string `json:"error_msg"` + Result OrderRefundInfo `json:"result"` +} + +type OrderRefundInfo struct { + OutTradeNo int64 `json:"out_trade_no"` // 商家订单号 + TransactionId string `json:"transaction_id"` // 第三方订单号 + RefundFee int64 `json:"refund_fee"` // 退款金额 + RefundOrderId int64 `json:"refund_order_id"` // 退款订单号 + RefundStatus int `json:"refund_status"` // 退款状态 0:未申请,1:退款中,2:退款成功,3:退款失败 + RefundSuccessTime string `json:"refund_success_time"` // 退款时间 +} + +// OrderRefund 订单退款 +func OrderRefund(c context.Context, orderRefundRequest OrderRefundRequest) OrderRefundResponse { + var err error + var info OrderRefundInfo + switch orderRefundRequest.PayChannel { + case payCommon.PAY_CHANNLE_TYPE_WECHAT: + // 微信H5支付 + info, err = WxOrderRefund(c, orderRefundRequest) + case payCommon.PAY_CHANNLE_TYPE_ZFB: + info, err = AliRefundOrder(c, orderRefundRequest) + default: + return OrderRefundResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: "暂不支持该支付渠道,请后续再使用", + } + } + if err != nil { + return OrderRefundResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: err.Error(), + } + } + return OrderRefundResponse{ + Code: payCommon.PAY_SUCCESS_CODE, + Result: info, + } +} + +type OrderRefundQueryRequest struct { + RefundOrderId int64 `json:"refund_order_id"` // 退款订单号 + OrderId int64 `json:"order_id"` // 支付订单号 + PayChannel int `json:"pay_channel"` // 支付渠道类型 1:wx,2:zfb + Wx WxPay `json:"wx"` + Ali AliPay `json:"ali"` +} +type OrderRefundQueryResponse struct { + Code int `json:"code"` + ErrorMsg string `json:"error_msg"` + Result OrderRefundInfo `json:"result"` +} + +// OrderRefundQuery 订单退款查询 +func OrderRefundQuery(c context.Context, orderRefundQueryRequest OrderRefundQueryRequest) OrderRefundQueryResponse { + var err error + var info OrderRefundInfo + switch orderRefundQueryRequest.PayChannel { + case payCommon.PAY_CHANNLE_TYPE_WECHAT: + // 微信H5支付 + info, err = WxOrderRefundQuery(c, orderRefundQueryRequest) + case payCommon.PAY_CHANNLE_TYPE_ZFB: + info, err = AliRefundOrderQuery(c, orderRefundQueryRequest) + default: + return OrderRefundQueryResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: "暂不支持该支付渠道,请后续再使用", + } + } + if err != nil { + return OrderRefundQueryResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: err.Error(), + } + } + return OrderRefundQueryResponse{ + Code: payCommon.PAY_SUCCESS_CODE, + Result: info, + } +} + +type OrderCloseRequest struct { + OrderId int64 `json:"order_id"` // 支付订单号 + PayChannel int `json:"pay_channel"` // 支付渠道类型 1:wx,2:zfb + Wx WxPay `json:"wx"` + Ali AliPay `json:"ali"` +} + +type OrderCloseResponse struct { + Code int `json:"code"` + ErrorMsg string `json:"error_msg"` + Result OrderCloseInfo `json:"result"` +} + +type OrderCloseInfo struct { + OutTradeNo int64 `json:"out_trade_no"` // 商家订单号 +} + +// OrderClose 关闭订单 +func OrderClose(c context.Context, orderCloseRequest OrderCloseRequest) OrderCloseResponse { + var err error + var info OrderCloseInfo + switch orderCloseRequest.PayChannel { + case payCommon.PAY_CHANNLE_TYPE_WECHAT: + // 微信H5支付 + info, err = WxCloseOrder(c, orderCloseRequest) + case payCommon.PAY_CHANNLE_TYPE_ZFB: + info, err = AliCloseOrder(c, orderCloseRequest) + default: + return OrderCloseResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: "暂不支持该支付渠道,请后续再使用", + } + } + if err != nil { + return OrderCloseResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: err.Error(), + } + } + return OrderCloseResponse{ + Code: payCommon.PAY_SUCCESS_CODE, + Result: info, + } +} diff --git a/app/third/paymentService/wechat_service.go b/app/third/paymentService/wechat_service.go index 3ad2c14..e439381 100644 --- a/app/third/paymentService/wechat_service.go +++ b/app/third/paymentService/wechat_service.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/go-pay/gopay" "github.com/go-pay/gopay/wechat/v3" + "strconv" "sync" "time" ) @@ -72,7 +73,8 @@ func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, er Set("description", payOrderRequest.Description). Set("out_trade_no", payOrderRequest.OrderId). Set("time_expire", expire). - Set("notify_url", payCommon.WX_NOTIFY_URL). + //Set("notify_url", payCommon.WX_NOTIFY_URL_TEST+payOrderRequest.Wx.AppId). + Set("notify_url", payCommon.WX_NOTIFY_URL_PROD+payOrderRequest.Wx.AppId). SetBodyMap("amount", func(bm gopay.BodyMap) { bm.Set("total", payOrderRequest.Amount). Set("currency", "CNY") @@ -94,32 +96,48 @@ func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, er return wxRsp.Response.H5Url, nil } -//func WxPayCallBack(notifyReq *wechat.V3NotifyReq) error { -// // 获取附加数据中的信息 -// appId := notifyReq.Resource.AssociatedData -// -// payOrderRequest := PayOrderRequest{} -// // 初始化微信客户端 -// InitClient(payOrderRequest) -// -// // 获取微信客户端 -// wxClient, err := GetClient() -// if err != nil { -// fmt.Println("Failed to get client:", err) -// return err -// } -// -// // 获取微信平台证书 -// certMap := wxClient.WxPublicKeyMap() -// // 验证异步通知的签名 -// err = notifyReq.VerifySignByPKMap(certMap) -// if err != nil { -// return err -// } -// objPtr := "" -// // 通用通知解密(推荐此方法) -// result := notifyReq.DecryptCipherTextToStruct(apiV3Key, objPtr) -//} +// WxPayCallBack 微信支付回调 +func WxPayCallBack(notifyReq *wechat.V3NotifyReq, wxConfig WxPay) error { + // 初始化微信客户端 + InitClient(wxConfig) + + // 获取微信客户端 + wxClient, err := GetClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return err + } + + // 获取微信平台证书 + certMap := wxClient.WxPublicKeyMap() + // 验证异步通知的签名 + err = notifyReq.VerifySignByPKMap(certMap) + if err != nil { + return err + } + var CallBackInfo struct { + AppId string `json:"app_id"` // 应用ID + Mchid string `json:"mchid"` // 商户号 + OutTradeNo string `json:"out_trade_no"` // 商户系统内部订单号 + TransactionId string `json:"transaction_id"` // 微信系统订单号 + TradeType string `json:"trade_type"` // JSAPI:公众号支付 NATIVE:扫码支付 App:App支付 MICROPAY:付款码支付 MWEB:H5支付 FACEPAY:刷脸支付 + TradeState string `json:"trade_state"` // SUCCESS:支付成功 REFUND:转入退款 NOTPAY:未支付 CLOSED:已关闭 REVOKED:已撤销(付款码支付) USERPAYING:用户支付中(付款码支付) PAYERROR:支付失败(其他原因,如银行返回失败) + TradeStateDesc string `json:"trade_state_desc"` // 交易状态描述 + SuccessTime string `json:"success_time"` // 支付完成时间 + Amount struct { + Total int64 `json:"total"` + PayerTotal int64 `json:"payer_total"` + } `json:"amount"` + } + // 通用通知解密(推荐此方法) + err = notifyReq.DecryptCipherTextToStruct(wxConfig.ApiV3Key, &CallBackInfo) + if err != nil { + return err + } + // todo 返回触发下游回调的格式 + + return nil +} // WxOrderQuery 查询微信订单 func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrderQueryInfo, error) { @@ -156,9 +174,10 @@ func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrder amountTotal := wxRsp.Response.Amount.Total payerTotal := wxRsp.Response.Amount.PayerTotal + outTradeNo, _ := strconv.Atoi(wxRsp.Response.OutTradeNo) return PayOrderQueryInfo{ AppId: wxRsp.Response.Appid, - OutTradeNo: wxRsp.Response.OutTradeNo, + OutTradeNo: int64(outTradeNo), TransactionId: wxRsp.Response.TransactionId, TradeState: tradeState, TradeStateDesc: wxRsp.Response.TradeStateDesc, @@ -167,3 +186,126 @@ func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrder PayerTotal: int64(payerTotal), }, nil } + +// WxOrderRefund 微信退款申请 +func WxOrderRefund(ctx context.Context, orderRefundRequest OrderRefundRequest) (OrderRefundInfo, error) { + // 初始化微信客户端 + InitClient(orderRefundRequest.Wx) + + // 获取微信客户端 + wxClient, err := GetClient() + if err != nil { + return OrderRefundInfo{}, err + } + // 初始化 BodyMap + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", orderRefundRequest.OrderId). + Set("sign_type", "MD5"). + // 必填 退款订单号(程序员定义的) + Set("out_refund_no", orderRefundRequest.RefundOrderId). + // 选填 退款描述 + Set("reason", orderRefundRequest.RefundReason). + SetBodyMap("amount", func(bm gopay.BodyMap) { + // 退款金额:单位是分 + bm.Set("refund", orderRefundRequest.RefundAmount). //实际退款金额 + Set("total", orderRefundRequest.RefundAmount). // 折扣前总金额(不是实际退款数) + Set("currency", "CNY") + }) + // body:参数Body + refund, err := wxClient.V3Refund(ctx, bm) + if err != nil { + return OrderRefundInfo{}, err + } + + // 将非正常退款异常记录 + if refund.Code != wechat.Success { + // 这里时对非正常退款的一些处理message,我们将code统一使用自定义的,然后把message抛出去 + return OrderRefundInfo{}, errors.New(fmt.Sprintf("申请退款接口失败,错误状态码:%d, 错误信息:%s", refund.Code, refund.Error)) + } + + outTradeNo, _ := strconv.Atoi(refund.Response.OutTradeNo) + outRefundNo, _ := strconv.Atoi(refund.Response.OutRefundNo) + refundStatus := payCommon.PAY_REFUND_STATU_COMMON + switch refund.Response.Status { + case "SUCCESS": + refundStatus = payCommon.PAY_REFUND_STATU_SUCCESS + case "CLOSED": + refundStatus = payCommon.PAY_REFUND_STATU_FAIL + case "PROCESSING": + refundStatus = payCommon.PAY_REFUND_STATU_ING + case "ABNORMAL": + refundStatus = payCommon.PAY_REFUND_STATU_FAIL + } + return OrderRefundInfo{ + OutTradeNo: int64(outTradeNo), + TransactionId: refund.Response.TransactionId, + RefundFee: int64(refund.Response.Amount.PayerRefund), + RefundOrderId: int64(outRefundNo), + RefundStatus: refundStatus, + RefundSuccessTime: refund.Response.SuccessTime, + }, nil +} + +// WxOrderRefundQuery 微信订单退款查询 +func WxOrderRefundQuery(ctx context.Context, orderRefundQueryRequest OrderRefundQueryRequest) (OrderRefundInfo, error) { + // 初始化微信客户端 + InitClient(orderRefundQueryRequest.Wx) + + // 获取微信客户端 + wxClient, err := GetClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return OrderRefundInfo{}, err + } + + wxRsp, err := wxClient.V3RefundQuery(ctx, strconv.FormatInt(orderRefundQueryRequest.RefundOrderId, 10), nil) + if err != nil { + return OrderRefundInfo{}, err + } + if wxRsp.Code != wechat.Success { + return OrderRefundInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error)) + } + outTradeNo, _ := strconv.Atoi(wxRsp.Response.OutTradeNo) + outRefundNo, _ := strconv.Atoi(wxRsp.Response.OutRefundNo) + refundStatus := payCommon.PAY_REFUND_STATU_COMMON + switch wxRsp.Response.Status { + case "SUCCESS": + refundStatus = payCommon.PAY_REFUND_STATU_SUCCESS + case "CLOSED": + refundStatus = payCommon.PAY_REFUND_STATU_FAIL + case "PROCESSING": + refundStatus = payCommon.PAY_REFUND_STATU_ING + case "ABNORMAL": + refundStatus = payCommon.PAY_REFUND_STATU_FAIL + } + return OrderRefundInfo{ + OutTradeNo: int64(outTradeNo), + TransactionId: wxRsp.Response.TransactionId, + RefundFee: int64(wxRsp.Response.Amount.PayerRefund), + RefundOrderId: int64(outRefundNo), + RefundStatus: refundStatus, + RefundSuccessTime: wxRsp.Response.SuccessTime, + }, nil +} + +// WxCloseOrder 微信订单关闭 +func WxCloseOrder(ctx context.Context, orderCloseRequest OrderCloseRequest) (OrderCloseInfo, error) { + // 初始化微信客户端 + InitClient(orderCloseRequest.Wx) + + // 获取微信客户端 + wxClient, err := GetClient() + if err != nil { + return OrderCloseInfo{}, err + } + wxRsp, err := wxClient.V3TransactionCloseOrder(ctx, "FY160932049419637602") + if err != nil { + return OrderCloseInfo{}, err + } + if wxRsp.Code != wechat.Success { + return OrderCloseInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error)) + } + return OrderCloseInfo{ + OutTradeNo: orderCloseRequest.OrderId, + }, nil +} diff --git a/app/utils/mq/mqmanager.go b/app/utils/mq/mqmanager.go index 8ac56b2..fc92de7 100644 --- a/app/utils/mq/mqmanager.go +++ b/app/utils/mq/mqmanager.go @@ -1,8 +1,6 @@ package mq import ( - common3 "PaymentCenter/app/constants/common" - mq "PaymentCenter/app/utils/mq/mqs" "sync" ) @@ -25,8 +23,8 @@ func (this *CMqManager) InitMq() { this.mqs = make(map[string]Imq) //this.mqs[common.MQ_RABBIT] = RabbitMq{} //this.mqs[common.MQ_NSQ] = NsqMq{} - this.mqs[common3.MQ_NATS] = mq.NatsMq{} - this.mqs[common3.MQ_KFK] = mq.KafkaMq{} + //this.mqs[common3.MQ_NATS] = mq.NatsMq{} + //this.mqs[common3.MQ_KFK] = mq.KafkaMq{} } func (this *CMqManager) GetMqByName(name string) Imq { once.Do(func() { From a79b18d2793aee1eaee9c5e27315872b7300cd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E4=BF=8A=E5=AE=8F?= <389838709@qq.com> Date: Fri, 2 Aug 2024 16:37:37 +0800 Subject: [PATCH 08/14] =?UTF-8?q?=E6=94=AF=E4=BB=98=E9=80=80=E6=AC=BE+?= =?UTF-8?q?=E9=80=80=E6=AC=BE=E5=8D=95=E6=9F=A5=E8=AF=A2+=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=85=B3=E9=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/http/requestmapping/front.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/app/http/requestmapping/front.go b/app/http/requestmapping/front.go index a4a71ac..a1a93aa 100644 --- a/app/http/requestmapping/front.go +++ b/app/http/requestmapping/front.go @@ -1,11 +1,3 @@ package requestmapping -import ( - "PaymentCenter/app/constants/common" - "PaymentCenter/app/http/entities/front" -) - -var FrontRequestMap = map[string]func() interface{}{ - common.FRONT_API_V1 + "/notify/wx": func() interface{} { return new(front.WxRequest) }, - common.FRONT_API_V1 + "/notify/ali": func() interface{} { return new(front.AliRequest) }, -} +var FrontRequestMap = map[string]func() interface{}{} From 4842aa47a31a46e45695e479dd1eb143900f1d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E4=BF=8A=E5=AE=8F?= <389838709@qq.com> Date: Fri, 2 Aug 2024 16:53:48 +0800 Subject: [PATCH 09/14] =?UTF-8?q?=E5=BF=85=E8=A6=81=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/third/paymentService/ali_service.go | 6 +++++- app/third/paymentService/payment_service.go | 4 ++++ app/third/paymentService/wechat_service.go | 8 ++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/third/paymentService/ali_service.go b/app/third/paymentService/ali_service.go index 0e85140..8fdf7f8 100644 --- a/app/third/paymentService/ali_service.go +++ b/app/third/paymentService/ali_service.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/go-pay/gopay" "github.com/go-pay/gopay/alipay" + "github.com/qit-team/snow-core/log/logger" "strconv" "sync" ) @@ -80,6 +81,7 @@ func ALiH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, e aliRsp, err := aliClient.TradeWapPay(c, bm) if err != nil { + logger.Error(c, "ALiH5PayInfo 发生错误", fmt.Sprintf("错误信息:%s", err.Error())) if bizErr, ok := alipay.IsBizError(err); ok { return "", bizErr } @@ -182,6 +184,7 @@ func AliRefundOrder(ctx context.Context, orderRefundRequest OrderRefundRequest) // 发起退款请求 aliRsp, err := aliClient.TradeRefund(ctx, bm) if err != nil { + logger.Error(ctx, "AliRefundOrder 发生错误", fmt.Sprintf("申请退款接口失败,错误信息:%s", err.Error())) if bizErr, ok := alipay.IsBizError(err); ok { return OrderRefundInfo{}, bizErr } @@ -253,9 +256,10 @@ func AliCloseOrder(ctx context.Context, orderCloseRequest OrderCloseRequest) (Or bm := make(gopay.BodyMap) bm.Set("out_trade_no", orderCloseRequest.OrderId) - // 条码支付 + // 关闭订单 aliRsp, err := aliClient.TradeClose(ctx, bm) if err != nil { + logger.Error(ctx, "AliCloseOrder 发生错误", fmt.Sprintf("申请退款接口失败,错误信息:%s", err.Error())) if bizErr, ok := alipay.IsBizError(err); ok { return OrderCloseInfo{}, bizErr } diff --git a/app/third/paymentService/payment_service.go b/app/third/paymentService/payment_service.go index 1c06d7a..cf9a369 100644 --- a/app/third/paymentService/payment_service.go +++ b/app/third/paymentService/payment_service.go @@ -3,6 +3,7 @@ package paymentService import ( "PaymentCenter/app/third/paymentService/payCommon" "context" + "github.com/qit-team/snow-core/log/logger" "strconv" ) @@ -40,6 +41,7 @@ type PayOrderResponse struct { // PaymentService 统一发起支付 func PaymentService(c context.Context, payOrderRequest PayOrderRequest) (payOrderResponse PayOrderResponse) { + logger.Info(c, "PaymentService 收到支付请求", payOrderRequest) var err error var info string switch payOrderRequest.ChannelType { @@ -147,6 +149,7 @@ type OrderRefundInfo struct { // OrderRefund 订单退款 func OrderRefund(c context.Context, orderRefundRequest OrderRefundRequest) OrderRefundResponse { + logger.Info(c, "PaymentService 收到退款请求", orderRefundRequest) var err error var info OrderRefundInfo switch orderRefundRequest.PayChannel { @@ -233,6 +236,7 @@ type OrderCloseInfo struct { // OrderClose 关闭订单 func OrderClose(c context.Context, orderCloseRequest OrderCloseRequest) OrderCloseResponse { + logger.Info(c, "PaymentService 收到关闭订单请求", orderCloseRequest) var err error var info OrderCloseInfo switch orderCloseRequest.PayChannel { diff --git a/app/third/paymentService/wechat_service.go b/app/third/paymentService/wechat_service.go index e439381..24c629b 100644 --- a/app/third/paymentService/wechat_service.go +++ b/app/third/paymentService/wechat_service.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/go-pay/gopay" "github.com/go-pay/gopay/wechat/v3" + "github.com/qit-team/snow-core/log/logger" "strconv" "sync" "time" @@ -62,7 +63,6 @@ func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, er // 获取微信客户端 wxClient, err := GetClient() if err != nil { - fmt.Println("Failed to get client:", err) return "", err } expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) @@ -91,6 +91,7 @@ func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, er return "", err } if wxRsp.Code != wechat.Success || wxRsp.Response.H5Url == "" { + logger.Error(c, "WxH5PayInfo 发生错误", fmt.Sprintf("错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error)) return "", errors.New(fmt.Sprintf("发起支付失败,失败状态码:%d, 失败原因:%s", wxRsp.Code, wxRsp.Error)) } return wxRsp.Response.H5Url, nil @@ -104,7 +105,6 @@ func WxPayCallBack(notifyReq *wechat.V3NotifyReq, wxConfig WxPay) error { // 获取微信客户端 wxClient, err := GetClient() if err != nil { - fmt.Println("Failed to get client:", err) return err } @@ -147,7 +147,6 @@ func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrder // 获取微信客户端 wxClient, err := GetClient() if err != nil { - fmt.Println("Failed to get client:", err) return PayOrderQueryInfo{}, err } @@ -219,6 +218,7 @@ func WxOrderRefund(ctx context.Context, orderRefundRequest OrderRefundRequest) ( // 将非正常退款异常记录 if refund.Code != wechat.Success { + logger.Error(ctx, "WxOrderRefund 发生错误", fmt.Sprintf("申请退款接口失败,错误状态码:%d, 错误信息:%s", refund.Code, refund.Error)) // 这里时对非正常退款的一些处理message,我们将code统一使用自定义的,然后把message抛出去 return OrderRefundInfo{}, errors.New(fmt.Sprintf("申请退款接口失败,错误状态码:%d, 错误信息:%s", refund.Code, refund.Error)) } @@ -254,7 +254,6 @@ func WxOrderRefundQuery(ctx context.Context, orderRefundQueryRequest OrderRefund // 获取微信客户端 wxClient, err := GetClient() if err != nil { - fmt.Println("Failed to get client:", err) return OrderRefundInfo{}, err } @@ -303,6 +302,7 @@ func WxCloseOrder(ctx context.Context, orderCloseRequest OrderCloseRequest) (Ord return OrderCloseInfo{}, err } if wxRsp.Code != wechat.Success { + logger.Error(ctx, "WxCloseOrder 发生错误", fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error)) return OrderCloseInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error)) } return OrderCloseInfo{ From 88b95cb67a19d2e9b062099689603cdbdcfd7be3 Mon Sep 17 00:00:00 2001 From: wolter Date: Fri, 2 Aug 2024 17:21:18 +0800 Subject: [PATCH 10/14] =?UTF-8?q?=E5=90=8E=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/console/command.go | 77 ++++++++++++++++++++++++ app/console/kernel.go | 4 +- app/constants/common/common.go | 7 +++ app/data/app.go | 4 ++ app/data/pay_channel.go | 16 +++-- app/http/controllers/backend/merchant.go | 1 + app/http/controllers/base.go | 23 +++++++ app/http/entities/backend/app.go | 3 +- app/http/entities/backend/merchant.go | 9 ++- app/http/entities/backend/pay_channel.go | 3 +- app/models/merchantmodel/merchant.go | 1 + app/services/app.go | 19 ++++++ app/services/merchant.go | 4 ++ app/services/pay_channel.go | 21 +++++++ bootstrap/bootstrap.go | 5 +- 15 files changed, 183 insertions(+), 14 deletions(-) diff --git a/app/console/command.go b/app/console/command.go index e312c14..c77f187 100644 --- a/app/console/command.go +++ b/app/console/command.go @@ -1,9 +1,86 @@ package console import ( + "PaymentCenter/app/constants/common" + "PaymentCenter/app/data" + "PaymentCenter/app/http/entities" + "PaymentCenter/app/models/ordersmodel" + "PaymentCenter/app/utils" "github.com/qit-team/snow-core/command" + "strconv" + "time" + "xorm.io/builder" ) func RegisterCommand(c *command.Command) { c.AddFunc("test", test) + c.AddFunc("closeOrder", closeOrder) +} + +// 关闭长时间支付中的订单 +func closeOrder() { + var now = time.Now().Format(time.DateTime) + utils.Log(nil, "关闭订单", now) + // 查询未支付的订单 + repo := data.NewOrderRepo(ordersmodel.GetInstance().GetDb()) + // 拼接条件 + cond := builder.NewCond() + cond = cond.And(builder.Eq{"status": common.ORDER_STATUS_PAYING}, builder.Lt{"create_time": time.Now().Add(-time.Hour)}) + + order := make([]ordersmodel.Orders, 0) + total, err := repo.OrderList(cond, entities.PageRequest{}, &order) + if err != nil { + utils.Log(nil, "关闭订单,查询未支付订单失败", err) + } else if total > 0 { + orderIds := make([]int64, 0) + for _, v := range order { + orderIds = append(orderIds, v.Id) + } + // 修改订单状态为关闭 + cond = builder.NewCond() + cond = cond.And(builder.In("id", orderIds)) + _, err = repo.OrderUpdate(&ordersmodel.Orders{Status: common.ORDER_STATUS_CLOSE}, cond, "status") + if err != nil { + utils.Log(nil, "关闭订单,修改订单状态失败", err) + return + } + } + utils.Log(nil, "关闭订单,修改订单状态成功", "count="+strconv.Itoa(len(order))) +} + +// 定时查询支付中的订单, 主动查询订单支付状态 +func queryOrder() { + var now = time.Now().Format(time.DateTime) + utils.Log(nil, "主动查询订单支付状态", now) + // 查询未支付的订单 + repo := data.NewOrderRepo(ordersmodel.GetInstance().GetDb()) + // 拼接条件 + cond := builder.NewCond() + cond = cond.And(builder.Eq{"status": common.ORDER_STATUS_PAYING}, builder.Gt{"create_time": time.Now().Add(-time.Second)}) + order := make([]ordersmodel.Orders, 0) + total, err := repo.OrderList(cond, entities.PageRequest{}, &order) + if err != nil { + utils.Log(nil, "主动查询订单支付状态,查询未付中订单失败", err) + return + } else if total > 0 { + // 发起查询上游支付 + for _, v := range order { + go func(order ordersmodel.Orders) { + // 发起查询 + utils.Log(nil, "主动查询订单支付状态,发起查询", order.Id) + // 解析上游结果 + + // 修改订单状态 + }(v) + } + } +} + +// 回调下游 +func callbackOrder() { + var now = time.Now().Format(time.DateTime) + utils.Log(nil, "回调下游", now) + // 查询回调失败的订单 + + // 发起回调 } diff --git a/app/console/kernel.go b/app/console/kernel.go index 23f081d..7ef72d9 100644 --- a/app/console/kernel.go +++ b/app/console/kernel.go @@ -11,5 +11,7 @@ import ( func RegisterSchedule(c *cron.Cron) { //c.AddFunc("0 30 * * * *", test) //c.AddFunc("@hourly", test) - c.AddFunc("@every 10s", test) + //c.AddFunc("@every 10s", test) + c.AddFunc("@every 60s", closeOrder) + c.AddFunc("@every 10s", queryOrder) } diff --git a/app/constants/common/common.go b/app/constants/common/common.go index d763935..9188762 100644 --- a/app/constants/common/common.go +++ b/app/constants/common/common.go @@ -20,4 +20,11 @@ const ( ADMIN_USER_ID = "User-Id" ADMIN_USER_NAME = "User-Name" ADMIN_USER_INCLUDEUSERS = "Include-Users" + + // '订单状态,待支付、支付中、支付成功、支付失败、订单关闭', + ORDER_STATUS_WAITPAY = 1 + ORDER_STATUS_PAYING = 2 + ORDER_STATUS_PAYED = 3 + ORDER_STATUS_FAILED = 4 + ORDER_STATUS_CLOSE = 5 ) diff --git a/app/data/app.go b/app/data/app.go index ff8c0fa..adabe1a 100644 --- a/app/data/app.go +++ b/app/data/app.go @@ -37,3 +37,7 @@ func (m *AppRepo) AppDelete(app *appmodel.App, conn builder.Cond) (int64, error) func (m *AppRepo) AppUpdate(app *appmodel.App, conn builder.Cond, columns ...string) (int64, error) { return m.repo.Where(conn).MustCols(columns...).Update(app) } + +func (m *AppRepo) AppGet(app *appmodel.App, conn builder.Cond) (bool, error) { + return m.repo.Where(conn).Get(app) +} diff --git a/app/data/pay_channel.go b/app/data/pay_channel.go index 5a26f22..8647765 100644 --- a/app/data/pay_channel.go +++ b/app/data/pay_channel.go @@ -25,15 +25,19 @@ func (m *PayChannelRepo) PayChannelList(conn builder.Cond, pageFilter entities.P return repo.Desc("create_time").FindAndCount(payChannelList) } -func (m *PayChannelRepo) PayChannelInsertOne(merchant *paychannelmodel.PayChannel) (int64, error) { - return m.repo.InsertOne(merchant) +func (m *PayChannelRepo) PayChannelInsertOne(payChannel *paychannelmodel.PayChannel) (int64, error) { + return m.repo.InsertOne(payChannel) } -func (m *PayChannelRepo) PayChannelDelete(merchant *paychannelmodel.PayChannel, conn builder.Cond) (int64, error) { - return m.repo.Where(conn).Delete(merchant) +func (m *PayChannelRepo) PayChannelDelete(payChannel *paychannelmodel.PayChannel, conn builder.Cond) (int64, error) { + return m.repo.Where(conn).Delete(payChannel) } // columns 参数为要更新的字段,即使为空 -func (m *PayChannelRepo) PayChannelUpdate(merchant *paychannelmodel.PayChannel, conn builder.Cond, columns ...string) (int64, error) { - return m.repo.Where(conn).MustCols(columns...).Update(merchant) +func (m *PayChannelRepo) PayChannelUpdate(payChannel *paychannelmodel.PayChannel, conn builder.Cond, columns ...string) (int64, error) { + return m.repo.Where(conn).MustCols(columns...).Update(payChannel) +} + +func (m *PayChannelRepo) PayChannelGet(payChannel *paychannelmodel.PayChannel, conn builder.Cond) (bool, error) { + return m.repo.Where(conn).Get(payChannel) } diff --git a/app/http/controllers/backend/merchant.go b/app/http/controllers/backend/merchant.go index 4f1e3cb..6832840 100644 --- a/app/http/controllers/backend/merchant.go +++ b/app/http/controllers/backend/merchant.go @@ -13,6 +13,7 @@ import ( func MerchantList(c *gin.Context) { req, _ := controllers.GetRequest(c).(*backend.MerchantListRequest) req.SetDefault() + req.GroupInfo = controllers.GetAdminUserIncludeUsers(c) merchantList, total, code := services.MerchantList(*req) result := make([]backend.MerchantResponse, 0, len(merchantList)) diff --git a/app/http/controllers/base.go b/app/http/controllers/base.go index 354472c..e097b95 100644 --- a/app/http/controllers/base.go +++ b/app/http/controllers/base.go @@ -1,6 +1,7 @@ package controllers import ( + "PaymentCenter/app/constants/common" "PaymentCenter/app/utils" "PaymentCenter/config" "bytes" @@ -17,6 +18,7 @@ import ( "net/http" "reflect" "regexp" + "strconv" "PaymentCenter/app/constants/errorcode" @@ -207,3 +209,24 @@ func phoneValidation(fl validator.FieldLevel) bool { reg := regexp.MustCompile(phoneRegex) return reg.MatchString(phone) } + +// 管理后台,获取用户id +func GetAdminId(c *gin.Context) int { + userIdStr, _ := c.Get(common.ADMIN_USER_ID) + if userIdStr != nil { + var userId, _ = strconv.Atoi(userIdStr.(string)) + return userId + } + + return 0 +} + +// 获取对应数据权限中拥有用户数据ID +func GetAdminUserIncludeUsers(c *gin.Context) string { + name, _ := c.Get(common.ADMIN_USER_INCLUDEUSERS) + if name != nil { + var temp, _ = name.(string) + return temp + } + return "" +} diff --git a/app/http/entities/backend/app.go b/app/http/entities/backend/app.go index dc4233b..11c0cf1 100644 --- a/app/http/entities/backend/app.go +++ b/app/http/entities/backend/app.go @@ -6,7 +6,8 @@ import ( ) type AppListRequest struct { - MerchantId int64 `json:"merchant_id"` + Id int64 `json:"id" form:"id"` + MerchantId int64 `json:"merchant_id" form:"merchant_id" validate:"required" label:"商户ID"` entities.PageRequest } diff --git a/app/http/entities/backend/merchant.go b/app/http/entities/backend/merchant.go index 1aaaedf..5ad7883 100644 --- a/app/http/entities/backend/merchant.go +++ b/app/http/entities/backend/merchant.go @@ -7,9 +7,10 @@ import ( type MerchantListRequest struct { entities.PageRequest - Name string `form:"name"` - Contact string `form:"contact"` - Phone string `form:"phone"` + Name string `form:"name"` + Contact string `form:"contact"` + Phone string `form:"phone"` + GroupInfo string } type MerchantResponse struct { @@ -35,6 +36,7 @@ type MerchantCreateRequest struct { Contact string `json:"contact" validate:"required" label:"联系人"` Phone string `json:"phone" validate:"required,phoneValidation" label:"联系电话"` Remark string `json:"remark" label:"备注"` + Creator int } func (m *MerchantCreateRequest) RequestToDb() (db merchantmodel.Merchant) { @@ -42,6 +44,7 @@ func (m *MerchantCreateRequest) RequestToDb() (db merchantmodel.Merchant) { db.Contact = m.Contact db.Phone = m.Phone db.Remark = m.Remark + db.Creator = m.Creator return db } diff --git a/app/http/entities/backend/pay_channel.go b/app/http/entities/backend/pay_channel.go index c2260ac..f1ad133 100644 --- a/app/http/entities/backend/pay_channel.go +++ b/app/http/entities/backend/pay_channel.go @@ -86,7 +86,8 @@ func (p *PayChannelCreateRequest) RequestToDb() (db paychannelmodel.PayChannel, } type PayChannelListRequest struct { - MerchantId int64 `json:"merchant_id"` + Id int64 `json:"id" form:"id" ` + MerchantId int64 `json:"merchant_id" form:"merchant_id" validate:"required" label:"商户ID"` entities.PageRequest } diff --git a/app/models/merchantmodel/merchant.go b/app/models/merchantmodel/merchant.go index f66ae6e..5dbb16d 100644 --- a/app/models/merchantmodel/merchant.go +++ b/app/models/merchantmodel/merchant.go @@ -18,6 +18,7 @@ type Merchant struct { Contact string `xorm:"'contact' varchar(128)"` Phone string `xorm:"'phone' varchar(11)"` Remark string `xorm:"'remark' varchar(1024)"` + Creator int `xorm:"'creator' int(10)"` CreateTime time.Time `xorm:"'create_time' datetime created"` UpdateTime time.Time `xorm:"'update_time' timestamp updated"` DeleteTime time.Time `xorm:"'delete_time' timestamp deleted"` diff --git a/app/services/app.go b/app/services/app.go index 7777b82..2c67441 100644 --- a/app/services/app.go +++ b/app/services/app.go @@ -18,6 +18,9 @@ func AppList(req backend.AppListRequest) (result []appmodel.App, total int64, co if req.MerchantId > 0 { conn = conn.And(builder.Eq{"merchant_id": req.MerchantId}) } + if req.Id > 0 { + conn = conn.And(builder.Eq{"id": req.Id}) + } // 调用repo appList := make([]appmodel.App, 0) @@ -78,3 +81,19 @@ func AppDelete(req entities.IdRequest) (code int) { code = handErr(err) return } + +func AppGet(app *appmodel.App) (code int) { + repo := data.NewAppRepo(appmodel.GetInstance().GetDb()) + // 拼接查询条件 + conn := builder.NewCond() + conn = conn.And(builder.Eq{"id": app.Id}) + has, err := repo.AppGet(app, conn) + if err != nil { + return handErr(err) + } + if !has { + return errorcode.AppNotFound + } + code = errorcode.Success + return +} diff --git a/app/services/merchant.go b/app/services/merchant.go index 9b31931..99d80f1 100644 --- a/app/services/merchant.go +++ b/app/services/merchant.go @@ -5,6 +5,7 @@ import ( "PaymentCenter/app/http/entities" "PaymentCenter/app/http/entities/backend" "PaymentCenter/app/models/merchantmodel" + "strings" "xorm.io/builder" ) @@ -22,6 +23,9 @@ func MerchantList(req backend.MerchantListRequest) (result []merchantmodel.Merch if req.Phone != "" { conn = conn.And(builder.Like{"phone", req.Phone}) } + if req.GroupInfo != "" { + conn = conn.And(builder.In("creator", strings.Split(req.GroupInfo, ","))) + } // 调用repo merchantList := make([]merchantmodel.Merchant, 0) diff --git a/app/services/pay_channel.go b/app/services/pay_channel.go index 3d3b57b..6ff06f9 100644 --- a/app/services/pay_channel.go +++ b/app/services/pay_channel.go @@ -17,6 +17,9 @@ func PayChannelList(req backend.PayChannelListRequest) (result []paychannelmodel if req.MerchantId > 0 { conn = conn.And(builder.Eq{"merchant_id": req.MerchantId}) } + if req.Id > 0 { + conn = conn.And(builder.Eq{"id": req.Id}) + } // 调用repo paychannelList := make([]paychannelmodel.PayChannel, 0) @@ -71,3 +74,21 @@ func PayChannelDelete(req entities.IdRequest) (code int) { code = handErr(err) return } + +func PayChannelGet(payChannel *paychannelmodel.PayChannel) (code int) { + repo := data.NewPayChannelRepo(paychannelmodel.GetInstance().GetDb()) + + // 拼接查询条件 + conn := builder.NewCond() + conn = conn.And(builder.Eq{"Id": payChannel.Id}) + has, err := repo.PayChannelGet(payChannel, conn) + if err != nil { + return handErr(err) + } + if !has { + return errorcode.PayChannelNotFound + } + + code = errorcode.Success + return +} diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 499fa77..ef9286c 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -4,11 +4,10 @@ import ( "PaymentCenter/app/jobs" "PaymentCenter/app/jobs/basejob" "PaymentCenter/config" - "github.com/qit-team/snow-core/log/accesslogger" - "github.com/qit-team/snow-core/db" "github.com/qit-team/snow-core/kernel/close" "github.com/qit-team/snow-core/kernel/container" + "github.com/qit-team/snow-core/log/accesslogger" "github.com/qit-team/snow-core/log/logger" "github.com/qit-team/snow-core/redis" ) @@ -26,6 +25,8 @@ func Bootstrap(conf *config.Config) (err error) { //注册db服务 //第一个参数为注入别名,第二个参数为配置,第三个参数可选为是否懒加载 err = db.Pr.Register(db.SingletonMain, conf.Db) + //cacher := caches.NewLRUCacher2(caches.NewMemoryStore(), time.Hour, 1000) + //db.GetDb().SetDefaultCacher(cacher) if err != nil { return From b8da95788bd8e7590de7e9eaa5d67f2d4218153d Mon Sep 17 00:00:00 2001 From: wolter Date: Fri, 2 Aug 2024 17:23:38 +0800 Subject: [PATCH 11/14] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E8=AF=B7=E6=B1=82=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/constants/errorcode/error_code.go | 8 +++++++- app/http/entities/backend/pay_channel.go | 15 ++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/constants/errorcode/error_code.go b/app/constants/errorcode/error_code.go index f240f37..5819d91 100644 --- a/app/constants/errorcode/error_code.go +++ b/app/constants/errorcode/error_code.go @@ -24,6 +24,10 @@ const ( // 商户 MerchantNotFound = 1100 + + AppNotFound = 1200 + + PayChannelNotFound = 1300 ) var MsgEN = map[int]string{ @@ -42,7 +46,9 @@ var MsgZH = map[int]string{ NotAuth: "未经授权", NotLogin: "未登录", - MerchantNotFound: "商户不存在", + MerchantNotFound: "商户不存在", + AppNotFound: "应用不存在", + PayChannelNotFound: "支付方式不存在", } var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH} diff --git a/app/http/entities/backend/pay_channel.go b/app/http/entities/backend/pay_channel.go index f1ad133..e6b3af2 100644 --- a/app/http/entities/backend/pay_channel.go +++ b/app/http/entities/backend/pay_channel.go @@ -49,16 +49,17 @@ type PayChannelCreateRequest struct { } type WechatPayChannel struct { - MchId string `json:"mch_id"` //直连商户号 - MchCertificateSerialNumber string `json:"mch_certificate_serial_number"` //商户证书序列号 - MchAPIv3Key string `json:"mch_APIv3_key"` //商户APIv3密钥 - PrivateKeyPath string `json:"private_key_path"` //商户私钥文件路径 + MchId string `json:"mch_id"` // 商户ID 或者服务商模式的 sp_mchid + SerialNo string `json:"serial_no"` // 商户证书的证书序列号 + ApiV3Key string `json:"api_v_3_key"` // apiV3Key,商户平台获取 + PrivateKey string `json:"private_key"` // 私钥 apiclient_key.pem 读取后的内容 } type AliPayPayChannel struct { - AliPublicKey string `json:"ali_public_key"` //支付宝公钥 - PrivateKeyPath string `json:"private_key_path"` //应用私钥 - SignType string `json:"sign_type"` //商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 + PrivateKey string `json:"private_key"` // 应用私钥 + AppPublicCert string `json:"app_public_cert"` // 应用公钥 + AlipayRootCert string `json:"alipay_root_cert"` // 支付宝根证书 + AlipayPublicCert string `json:"alipay_public_cert"` // 支付宝公钥 } func (p *PayChannelCreateRequest) RequestToDb() (db paychannelmodel.PayChannel, err error) { From f1bb1ad53ba11c037830daaf9f376f63a64ddcbb Mon Sep 17 00:00:00 2001 From: wolter Date: Fri, 2 Aug 2024 17:32:49 +0800 Subject: [PATCH 12/14] fix --- app/services/pay_channel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/pay_channel.go b/app/services/pay_channel.go index 6ff06f9..6cfe4f6 100644 --- a/app/services/pay_channel.go +++ b/app/services/pay_channel.go @@ -80,7 +80,7 @@ func PayChannelGet(payChannel *paychannelmodel.PayChannel) (code int) { // 拼接查询条件 conn := builder.NewCond() - conn = conn.And(builder.Eq{"Id": payChannel.Id}) + conn = conn.And(builder.Eq{"id": payChannel.Id}) has, err := repo.PayChannelGet(payChannel, conn) if err != nil { return handErr(err) From fb5c0e458b9eeb8ad7e0b061aa62520ade92bea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E4=BF=8A=E5=AE=8F?= <389838709@qq.com> Date: Fri, 2 Aug 2024 18:14:04 +0800 Subject: [PATCH 13/14] =?UTF-8?q?=E5=9B=9E=E8=B0=83=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/front/payment_controller.go | 76 ++++++++++++++++--- app/http/routes/route.go | 4 +- app/third/paymentService/ali_service.go | 4 +- app/third/paymentService/payCommon/common.go | 8 +- app/third/paymentService/payment_service.go | 1 + app/third/paymentService/wechat_service.go | 4 +- 6 files changed, 78 insertions(+), 19 deletions(-) diff --git a/app/http/controllers/front/payment_controller.go b/app/http/controllers/front/payment_controller.go index f22a12f..a21e05d 100644 --- a/app/http/controllers/front/payment_controller.go +++ b/app/http/controllers/front/payment_controller.go @@ -1,28 +1,53 @@ package front import ( + "PaymentCenter/app/constants/common" + "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/models/paychannelmodel" + "PaymentCenter/app/services" "PaymentCenter/app/third/paymentService" + "encoding/json" + "fmt" "github.com/gin-gonic/gin" "github.com/go-pay/gopay" "github.com/go-pay/gopay/alipay" "github.com/go-pay/gopay/wechat/v3" "github.com/go-pay/xlog" "github.com/qit-team/snow-core/log/logger" + "strconv" "net/http" ) // WxCallback 微信支付回调 func WxCallback(c *gin.Context) { - appId := c.Param("appId") - logger.Info(c, "WxCallback-回调数据APPID", appId) - if appId == "" { + payChannelId := c.Param("payChannelId") + logger.Info(c, "WxCallback-回调数据payChannelId", payChannelId) + if payChannelId == "" { c.String(http.StatusBadRequest, "%s", "fail") } - // todo 查询应用下的支付配置 + // 查询应用下的支付配置 + var payChannelModel paychannelmodel.PayChannel + payChannelIdInt, _ := strconv.Atoi(payChannelId) + payChannelModel.Id = int64(payChannelIdInt) + services.PayChannelGet(&payChannelModel) + if payChannelModel.ChannelType != common.PAY_CHANNEL_WECHAT_H5 { + logger.Error(c, "WxCallback-回调数据解析支付配置错误,查询的数据不是当前渠道") + c.String(http.StatusBadRequest, "%s", "fail") + } var wxConfig paymentService.WxPay + err := json.Unmarshal([]byte(payChannelModel.ExtJson), &wxConfig) + if err != nil { + logger.Error(c, "WxCallback-回调数据解析支付配置错误", fmt.Sprintf("错误原因:%s", err.Error())) + c.String(http.StatusBadRequest, "%s", "fail") + } + if wxConfig.ApiV3Key == "" || wxConfig.MchId == "" || wxConfig.PrivateKey == "" || wxConfig.SerialNo == "" { + logger.Error(c, "WxCallback-回调数据解析支付配置错误,解析出来的信息为空") + c.String(http.StatusBadRequest, "%s", "fail") + } + wxConfig.AppId = payChannelModel.AppId logger.Info(c, "WxCallback-回调数据", c.Request) notifyReq, err := wechat.V3ParseNotify(c.Request) @@ -45,12 +70,46 @@ func WxCallback(c *gin.Context) { // AliCallback 支付宝支付回调 func AliCallback(c *gin.Context) { - appId := c.Param("appId") - logger.Info(c, "AliCallback-回调数据APPID", appId) - if appId == "" { + payChannelId := c.Param("payChannelId") + logger.Info(c, "AliCallback-回调数据APPID", payChannelId) + if payChannelId == "" { c.String(http.StatusBadRequest, "%s", "fail") } - // todo 查询应用下的支付配置 + // 查询应用下的支付配置 + var payChannelModel paychannelmodel.PayChannel + payChannelIdInt, _ := strconv.Atoi(payChannelId) + payChannelModel.Id = int64(payChannelIdInt) + code := services.PayChannelGet(&payChannelModel) + if payChannelModel.ChannelType != common.PAY_CHANNEL_ALIPAY_WEB { + logger.Error(c, "AliCallback-回调数据解析支付配置错误,查询的数据不是当前渠道") + c.String(http.StatusBadRequest, "%s", "fail") + } + if code == errorcode.PayChannelNotFound { + logger.Error(c, "AliCallback-回调数据未获取到支付配置,404") + c.String(http.StatusBadRequest, "%s", "fail") + } + + var aliConfig paymentService.AliPay + var aliConfigModel struct { + PrivateKey string `json:"private_key"` // 应用私钥 + AppPublicCert string `json:"app_public_cert"` // 应用公钥 + AlipayRootCert string `json:"alipay_root_cert"` // 支付宝根证书 + AlipayPublicCert string `json:"alipay_public_cert"` // 支付宝公钥 + } + err := json.Unmarshal([]byte(payChannelModel.ExtJson), &aliConfigModel) + if err != nil { + logger.Error(c, "AliCallback-回调数据解析支付配置错误", fmt.Sprintf("错误原因:%s", err.Error())) + c.String(http.StatusBadRequest, "%s", "fail") + } + if aliConfigModel.AlipayPublicCert == "" || aliConfigModel.PrivateKey == "" || aliConfigModel.AppPublicCert == "" || aliConfigModel.AlipayRootCert == "" { + logger.Error(c, "AliCallback-回调数据解析支付配置错误,解析出来的信息为空") + c.String(http.StatusBadRequest, "%s", "fail") + } + aliConfig.AppId = payChannelModel.AppId + aliConfig.PrivateKey = aliConfigModel.PrivateKey + aliConfig.AppPublicCert = []byte(aliConfigModel.AppPublicCert) + aliConfig.AlipayRootCert = []byte(aliConfigModel.AlipayRootCert) + aliConfig.AlipayPublicCert = []byte(aliConfigModel.AlipayPublicCert) notifyReq, err := alipay.ParseNotifyToBodyMap(c.Request) // c.Request 是 gin 框架的写法 logger.Info(c, "AliCallback-回调数据", c.Request) @@ -58,7 +117,6 @@ func AliCallback(c *gin.Context) { xlog.Error(err) return } - var aliConfig paymentService.AliPay err = paymentService.ALiCallBack(notifyReq, aliConfig) if err != nil { logger.Error(c, "AliCallback-回调执行失败,失败原因:", err.Error()) diff --git a/app/http/routes/route.go b/app/http/routes/route.go index a3fe510..8c1f59e 100644 --- a/app/http/routes/route.go +++ b/app/http/routes/route.go @@ -55,8 +55,8 @@ func RegisterRoute(router *gin.Engine) { //回调处理 notify := v1.Group("/notify") { - notify.POST("/wx/:appId", front.WxCallback) - notify.POST("/ali/:appId", front.AliCallback) + notify.POST("/wx/:payChannelId", front.WxCallback) + notify.POST("/ali/:payChannelId", front.AliCallback) } } diff --git a/app/third/paymentService/ali_service.go b/app/third/paymentService/ali_service.go index 8fdf7f8..df3e6a7 100644 --- a/app/third/paymentService/ali_service.go +++ b/app/third/paymentService/ali_service.go @@ -76,8 +76,8 @@ func ALiH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, e bm.Set("out_trade_no", payOrderRequest.OrderId). Set("total_amount", amount). Set("subject", payOrderRequest.Description). - //Set("notify_url", payCommon.ALI_NOTIFY_URL_TEST+payOrderRequest.Ali.AppId). - Set("notify_url", payCommon.ALI_NOTIFY_URL_PROD+payOrderRequest.Ali.AppId) + //Set("notify_url", fmt.Sprintf(payCommon.ALI_NOTIFY_URL_TEST+"%d", payOrderRequest.PayChannelId)). + Set("notify_url", fmt.Sprintf(payCommon.ALI_NOTIFY_URL_PROD+"%d", payOrderRequest.PayChannelId)) aliRsp, err := aliClient.TradeWapPay(c, bm) if err != nil { diff --git a/app/third/paymentService/payCommon/common.go b/app/third/paymentService/payCommon/common.go index 7c376df..bf2c8c5 100644 --- a/app/third/paymentService/payCommon/common.go +++ b/app/third/paymentService/payCommon/common.go @@ -21,10 +21,10 @@ const ( WX_SUCCESS_CODE = 200 // 微信状态码返回成功 - WX_NOTIFY_URL_TEST = TEST_URL + "/v1/notify/wx/appId" // 微信支付回调地址 - WX_NOTIFY_URL_PROD = PROD_URL + "/v1/notify/wx/appId" // 微信支付回调地址 - ALI_NOTIFY_URL_TEST = TEST_URL + "/v1/notify/ali/appId" // 支付宝支付回调地址 - ALI_NOTIFY_URL_PROD = PROD_URL + "/v1/notify/ali/appId" // 支付宝支付回调地址 + WX_NOTIFY_URL_TEST = TEST_URL + "/v1/notify/wx/" // 微信支付回调地址 + WX_NOTIFY_URL_PROD = PROD_URL + "/v1/notify/wx/" // 微信支付回调地址 + ALI_NOTIFY_URL_TEST = TEST_URL + "/v1/notify/ali/" // 支付宝支付回调地址 + ALI_NOTIFY_URL_PROD = PROD_URL + "/v1/notify/ali/" // 支付宝支付回调地址 ORDER_NO_TYPE_ORDER_NO = 2 diff --git a/app/third/paymentService/payment_service.go b/app/third/paymentService/payment_service.go index cf9a369..6f5e00b 100644 --- a/app/third/paymentService/payment_service.go +++ b/app/third/paymentService/payment_service.go @@ -8,6 +8,7 @@ import ( ) type PayOrderRequest struct { + PayChannelId int64 `json:"pay_channel_id"` // 支付方式ID OrderId int64 `json:"order_id"` // 平台订单号 ChannelType int `json:"channel_type"` // 支付方式 Description string `json:"description"` // 商品描述 diff --git a/app/third/paymentService/wechat_service.go b/app/third/paymentService/wechat_service.go index 24c629b..f7c9b03 100644 --- a/app/third/paymentService/wechat_service.go +++ b/app/third/paymentService/wechat_service.go @@ -73,8 +73,8 @@ func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, er Set("description", payOrderRequest.Description). Set("out_trade_no", payOrderRequest.OrderId). Set("time_expire", expire). - //Set("notify_url", payCommon.WX_NOTIFY_URL_TEST+payOrderRequest.Wx.AppId). - Set("notify_url", payCommon.WX_NOTIFY_URL_PROD+payOrderRequest.Wx.AppId). + //Set("notify_url", fmt.Sprintf(payCommon.WX_NOTIFY_URL_TEST+"%d", payOrderRequest.PayChannelId)). + Set("notify_url", fmt.Sprintf(payCommon.WX_NOTIFY_URL_PROD+"%d", payOrderRequest.PayChannelId)). SetBodyMap("amount", func(bm gopay.BodyMap) { bm.Set("total", payOrderRequest.Amount). Set("currency", "CNY") From aa1481b923ffefbf37d223ce78d7281d817fae07 Mon Sep 17 00:00:00 2001 From: Rzy <465386466@qq.com> Date: Fri, 2 Aug 2024 18:38:51 +0800 Subject: [PATCH 14/14] =?UTF-8?q?=E5=90=88=E5=B9=B6=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/constants/common/common.go | 2 +- app/http/entities/backend/app.go | 12 +-- app/http/routes/route.go | 13 +-- app/services/app.go | 10 +- app/utils/mq/mqs/kafka.go | 174 +++++++++++++++---------------- go.mod | 7 +- 6 files changed, 101 insertions(+), 117 deletions(-) diff --git a/app/constants/common/common.go b/app/constants/common/common.go index 328530c..c961f93 100644 --- a/app/constants/common/common.go +++ b/app/constants/common/common.go @@ -3,7 +3,7 @@ package common const ( TOKEN_PRE = "player_token_" TOKEN_Admin = "Admin_token_" - ADMIN_V1 = "/pay/admin/api/v1 + ADMIN_V1 = "/pay/admin/api/v1" FRONT_V1 = "/api/v1" // 支付渠道枚举,1微信JSAPI,2微信H5,3微信app,4微信Native,5微信小程序,6支付宝网页&移动应用,7支付宝小程序,8支付宝JSAPI diff --git a/app/http/entities/backend/app.go b/app/http/entities/backend/app.go index 11c0cf1..9956684 100644 --- a/app/http/entities/backend/app.go +++ b/app/http/entities/backend/app.go @@ -16,8 +16,8 @@ type AppResponse struct { MerchantId int64 `json:"merchant_id"` AppName string `json:"app_name"` AppRemark string `json:"app_remark"` - Status int `json:"status"` - KeyType int `json:"key_type"` + Status int32 `json:"status"` + KeyType int32 `json:"key_type"` PublicKey string `json:"public_key"` PrivateKey string `json:"private_key"` MerchantPublicKey string `json:"merchant_public_key"` @@ -45,8 +45,8 @@ type AppCreateRequest struct { MerchantId int64 `json:"merchant_id" validate:"required" label:"商户ID"` AppName string `json:"app_name" validate:"required" label:"应用名称"` AppRemark string `json:"app_remark" label:"应用备注"` - Status int `json:"status" validate:"oneof=0 1 2" label:"应用状态"` - KeyType int `json:"key_type" validate:"required" label:"应用密钥类型"` + Status int32 `json:"status" validate:"oneof=0 1 2" label:"应用状态"` + KeyType int32 `json:"key_type" validate:"required" label:"应用密钥类型"` PublicKey string `json:"public_key" validate:"required" label:"应用公钥"` PrivateKey string `json:"private_key" validate:"required" label:"应用私钥"` MerchantPublicKey string `json:"merchant_public_key" label:"商户公钥"` @@ -72,8 +72,8 @@ type AppUpdateRequest struct { Id int64 `json:"id" validate:"required" label:"应用ID"` AppName string `json:"app_name" label:"应用名称"` AppRemark string `json:"app_remark" label:"应用备注"` - Status int `json:"status" label:"应用状态"` - KeyType int `json:"key_type" label:"应用密钥类型"` + Status int32 `json:"status" label:"应用状态"` + KeyType int32 `json:"key_type" label:"应用密钥类型"` PublicKey string `json:"public_key" label:"应用公钥"` PrivateKey string `json:"private_key" label:"应用私钥"` MerchantPublicKey string `json:"merchant_public_key" label:"商户公钥"` diff --git a/app/http/routes/route.go b/app/http/routes/route.go index f4366f9..e0dc7ff 100644 --- a/app/http/routes/route.go +++ b/app/http/routes/route.go @@ -4,14 +4,15 @@ package routes * 配置路由 */ import ( - "PaymentCenter/app/constants/common" "PaymentCenter/app/http/controllers" - "PaymentCenter/app/http/controllers/front" "PaymentCenter/app/http/controllers/backend" + "PaymentCenter/app/http/controllers/front" "PaymentCenter/app/http/middlewares" "PaymentCenter/app/http/trace" "PaymentCenter/app/utils/metric" "PaymentCenter/config" + ginSwagger "github.com/swaggo/gin-swagger" + "github.com/swaggo/gin-swagger/swaggerFiles" "github.com/gin-gonic/gin" "github.com/qit-team/snow-core/http/middleware" @@ -45,8 +46,6 @@ func RegisterRoute(router *gin.Engine) { router.NoRoute(controllers.Error404) //router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) - v1 := router.Group(common.FRONT_V1, middlewares.ValidateRequest()) - { v1 := router.Group("/v1") { @@ -59,8 +58,6 @@ func RegisterRoute(router *gin.Engine) { } router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) - pay := v1.Group("pay", middlewares.ValidatePayRequest()) - pay.POST("web", backend.MerchantList) // 商户列表 - } - + pay := v1.Group("pay", middlewares.ValidatePayRequest()) + pay.POST("web", backend.MerchantList) // 商户列表 } diff --git a/app/services/app.go b/app/services/app.go index 9b1211c..a161494 100644 --- a/app/services/app.go +++ b/app/services/app.go @@ -65,15 +65,7 @@ func AppUpdate(app *appmodel.App) (code int) { } else { _, err = repo.AppUpdate(app, conn) } - if !has { - return errorcode.MerchantNotFound - } - - // 拼接查询条件 - uconn := builder.NewCond() - uconn = uconn.And(builder.Eq{"Id": App.Id}) - _, err = repo.AppUpdate(App, uconn, "app_remark") - + code = handErr(err) return } diff --git a/app/utils/mq/mqs/kafka.go b/app/utils/mq/mqs/kafka.go index 5208608..249a8b6 100644 --- a/app/utils/mq/mqs/kafka.go +++ b/app/utils/mq/mqs/kafka.go @@ -1,89 +1,89 @@ package mq -import ( - "PaymentCenter/app/utils" - "PaymentCenter/config" - "context" - "encoding/json" - "fmt" - "github.com/Shopify/sarama" - "github.com/qit-team/snow-core/redis" - - "strconv" - "sync" -) - -type KafkaMq struct { -} - -// 同步 -func (n KafkaMq) Produce(name string, log interface{}, delayTime int, args ...interface{}) error { - kafConfig := sarama.NewConfig() - kafConfig.Producer.RequiredAcks = sarama.WaitForAll // 发送完数据需要leader和follow都确认 - kafConfig.Producer.Partitioner = sarama.NewRandomPartitioner // 新选出一个partition - kafConfig.Producer.Return.Successes = true // 成功交付的消息将在success channel返回 - - // 构造一个消息 - msg := &sarama.ProducerMessage{} - msg.Topic = name - var data, _ = json.Marshal(log) - msg.Value = sarama.StringEncoder(string(data)) - // 连接kafka - client, err := sarama.NewSyncProducer(config.GetConf().KafkaUrl, kafConfig) - if err != nil { - fmt.Println("producer closed, err:", err) - return nil - } - defer client.Close() - // 发送消息 - pid, offset, err := client.SendMessage(msg) - if err != nil { - utils.Log(nil, "send msg failed, err:", err, pid, offset) - return nil - } - return nil -} - -func (n KafkaMq) Consume(name string, hand interface{}) { - consumer, err := sarama.NewConsumer(config.GetConf().KafkaUrl, nil) - if err != nil { - utils.Log(nil, "kafka comsume", err.Error()) - return - } - partitionList, err := consumer.Partitions(name) // 根据topic取到所有的分区 - if err != nil { - utils.Log(nil, "kafka comsume", err.Error()) - return - } - //utils.Log(nil,"kafka comsume",name,partitionList) - for partition := range partitionList { // 遍历所有的分区 - // 针对每个分区创建一个对应的分区消费者 - var offsetReDis, _ = redis.GetRedis().Incr(context.Background(), "kafka_consume:"+strconv.Itoa(int(partition))).Result() //保证多消费者不重复消费 - var offset int64 = sarama.OffsetNewest - if offsetReDis > 0 { - //offset = int64(offsetReDis) - } - pc, err := consumer.ConsumePartition(name, int32(partition), offset) - //utils.Log(nil,"partion",int32(partition)) - if err != nil { - fmt.Printf("failed to start consumer for partition %d,err:%v\n", partition, err) - return - } - defer pc.AsyncClose() - var wg sync.WaitGroup - wg.Add(1) - // 异步从每个分区消费信息 - go func(sarama.PartitionConsumer) { - for msg := range pc.Messages() { - defer wg.Done() - var handler = hand.(func(tag uint64, ch interface{}, msg []byte)) - handler(0, nil, msg.Value) - //utils.Log(nil,"hand msg",string(msg.Value),msg.Offset) - } - }(pc) - wg.Wait() - } -} -func (n KafkaMq) DelyConsume(name string, hand interface{}) { - -} +// +//import ( +// "PaymentCenter/app/utils" +// "PaymentCenter/config" +// "context" +// "fmt" +// "github.com/Shopify/sarama" +// "github.com/qit-team/snow-core/redis" +// +// "strconv" +// "sync" +//) +// +//type KafkaMq struct { +//} +// +//// 同步 +//func (n KafkaMq) Produce(name string, log interface{}, delayTime int, args ...interface{}) error { +// //kafConfig := sarama.NewConfig() +// //kafConfig.Producer.RequiredAcks = sarama.WaitForAll // 发送完数据需要leader和follow都确认 +// //kafConfig.Producer.Partitioner = sarama.NewRandomPartitioner // 新选出一个partition +// //kafConfig.Producer.Return.Successes = true // 成功交付的消息将在success channel返回 +// // +// //// 构造一个消息 +// //msg := &sarama.ProducerMessage{} +// //msg.Topic = name +// //var data, _ = json.Marshal(log) +// //msg.Value = sarama.StringEncoder(string(data)) +// //// 连接kafka +// //client, err := sarama.NewSyncProducer(config.GetConf().KafkaUrl, kafConfig) +// //if err != nil { +// // fmt.Println("producer closed, err:", err) +// // return nil +// //} +// //defer client.Close() +// //// 发送消息 +// //pid, offset, err := client.SendMessage(msg) +// //if err != nil { +// // utils.Log(nil, "send msg failed, err:", err, pid, offset) +// // return nil +// //} +// return nil +//} +// +//func (n KafkaMq) Consume(name string, hand interface{}) { +// consumer, err := sarama.NewConsumer(config.GetConf().KafkaUrl, nil) +// if err != nil { +// utils.Log(nil, "kafka comsume", err.Error()) +// return +// } +// partitionList, err := consumer.Partitions(name) // 根据topic取到所有的分区 +// if err != nil { +// utils.Log(nil, "kafka comsume", err.Error()) +// return +// } +// //utils.Log(nil,"kafka comsume",name,partitionList) +// for partition := range partitionList { // 遍历所有的分区 +// // 针对每个分区创建一个对应的分区消费者 +// var offsetReDis, _ = redis.GetRedis().Incr(context.Background(), "kafka_consume:"+strconv.Itoa(int(partition))).Result() //保证多消费者不重复消费 +// var offset int64 = sarama.OffsetNewest +// if offsetReDis > 0 { +// //offset = int64(offsetReDis) +// } +// pc, err := consumer.ConsumePartition(name, int32(partition), offset) +// //utils.Log(nil,"partion",int32(partition)) +// if err != nil { +// fmt.Printf("failed to start consumer for partition %d,err:%v\n", partition, err) +// return +// } +// defer pc.AsyncClose() +// var wg sync.WaitGroup +// wg.Add(1) +// // 异步从每个分区消费信息 +// go func(sarama.PartitionConsumer) { +// for msg := range pc.Messages() { +// defer wg.Done() +// var handler = hand.(func(tag uint64, ch interface{}, msg []byte)) +// handler(0, nil, msg.Value) +// //utils.Log(nil,"hand msg",string(msg.Value),msg.Offset) +// } +// }(pc) +// wg.Wait() +// } +//} +//func (n KafkaMq) DelyConsume(name string, hand interface{}) { +// +//} diff --git a/go.mod b/go.mod index cc9e3a3..fdcd3e0 100644 --- a/go.mod +++ b/go.mod @@ -117,17 +117,12 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.21.0 // indirect + golang.org/x/arch v0.6.0 // indirect golang.org/x/crypto v0.25.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect - golang.org/x/arch v0.6.0 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.1.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect