添加支付宝和微信充值功能,重构交易处理逻辑,优化资源创建与支付链接生成

This commit is contained in:
2025-04-18 16:22:38 +08:00
parent f6a97545c5
commit a7e59fb1d7
14 changed files with 939 additions and 224 deletions

View File

@@ -22,6 +22,8 @@
- [ ] Limiter - [ ] Limiter
- [ ] Compress - [ ] Compress
transition 服务,查询后立即完成,提供是否访问接口参数,统一主动与回调调用
callback 结果直接由 api 端提供,不通过前端转发 callback 结果直接由 api 端提供,不通过前端转发
统一套餐创建逻辑 统一套餐创建逻辑

1
go.mod
View File

@@ -21,6 +21,7 @@ require (
require ( require (
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.6 // indirect
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect github.com/andybalholm/brotli v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect

160
go.sum
View File

@@ -1,18 +1,56 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8=
github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.6 h1:XPck0S+7I8se2e8YnjA/D9OE6FzXwWo2UjlSziJs+z0=
github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.6/go.mod h1:Ia7UzBNwNCeNR3TxGQAWi6yNttIFWGiR4HzIlA3Dasc=
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
github.com/alibabacloud-go/openapi-util v0.1.1/go.mod h1:/UehBSE2cf1gYT43GV4E+RxTdLRzURImCYY0aRmlXpw=
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0= github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8= github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -20,6 +58,9 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
@@ -35,12 +76,30 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 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/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/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
@@ -57,10 +116,15 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw= github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc= github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y=
github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
@@ -75,8 +139,15 @@ github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBW
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@@ -93,16 +164,23 @@ github.com/smartwalle/ngx v1.0.9 h1:pUXDvWRZJIHVrCKA1uZ15YwNti+5P4GuJGbpJ4WvpMw=
github.com/smartwalle/ngx v1.0.9/go.mod h1:mx/nz2Pk5j+RBs7t6u6k22MPiBG/8CtOMpCnALIG8Y0= github.com/smartwalle/ngx v1.0.9/go.mod h1:mx/nz2Pk5j+RBs7t6u6k22MPiBG/8CtOMpCnALIG8Y0=
github.com/smartwalle/nsign v1.0.9 h1:8poAgG7zBd8HkZy9RQDwasC6XZvJpDGQWSjzL2FZL6E= github.com/smartwalle/nsign v1.0.9 h1:8poAgG7zBd8HkZy9RQDwasC6XZvJpDGQWSjzL2FZL6E=
github.com/smartwalle/nsign v1.0.9/go.mod h1:eY6I4CJlyNdVMP+t6z1H6Jpd4m5/V+8xi44ufSTxXgc= github.com/smartwalle/nsign v1.0.9/go.mod h1:eY6I4CJlyNdVMP+t6z1H6Jpd4m5/V+8xi44ufSTxXgc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI= github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
@@ -111,53 +189,133 @@ github.com/wechatpay-apiv3/wechatpay-go v0.2.20 h1:gS8oFn1bHGnyapR2Zb4aqTV6l4kJW
github.com/wechatpay-apiv3/wechatpay-go v0.2.20/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q= github.com/wechatpay-apiv3/wechatpay-go v0.2.20/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -183,6 +341,8 @@ gorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o=
gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg= gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg=
gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU= gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU=
gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE= gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=

View File

@@ -706,6 +706,7 @@ create table trade (
payment decimal(12, 2) not null default 0, payment decimal(12, 2) not null default 0,
method int not null, method int not null,
status int not null default 0, status int not null default 0,
pay_url text,
paid_at timestamp, paid_at timestamp,
cancel_at timestamp, cancel_at timestamp,
created_at timestamp default current_timestamp, created_at timestamp default current_timestamp,
@@ -731,6 +732,7 @@ comment on column trade.amount is '订单总金额';
comment on column trade.payment is '支付金额'; comment on column trade.payment is '支付金额';
comment on column trade.method is '支付方式1-支付宝2-微信'; comment on column trade.method is '支付方式1-支付宝2-微信';
comment on column trade.status is '订单状态0-待支付1-已支付2-已取消3-已退款'; comment on column trade.status is '订单状态0-待支付1-已支付2-已取消3-已退款';
comment on column trade.pay_url is '支付链接';
comment on column trade.paid_at is '支付时间'; comment on column trade.paid_at is '支付时间';
comment on column trade.cancel_at is '取消时间'; comment on column trade.cancel_at is '取消时间';
comment on column trade.created_at is '创建时间'; comment on column trade.created_at is '创建时间';

View File

@@ -5,6 +5,8 @@ import (
"platform/pkg/env" "platform/pkg/env"
"github.com/wechatpay-apiv3/wechatpay-go/core" "github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
"github.com/wechatpay-apiv3/wechatpay-go/core/option" "github.com/wechatpay-apiv3/wechatpay-go/core/option"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/native" "github.com/wechatpay-apiv3/wechatpay-go/services/payments/native"
"github.com/wechatpay-apiv3/wechatpay-go/utils" "github.com/wechatpay-apiv3/wechatpay-go/utils"
@@ -14,10 +16,12 @@ var WechatPay *WechatPayClient
type WechatPayClient struct { type WechatPayClient struct {
Native *native.NativeApiService Native *native.NativeApiService
Notify *notify.Handler
} }
func InitWechatPay() { func InitWechatPay() {
// 加载 rsa 密钥文件
appPrivateKey, err := utils.LoadPrivateKey(env.WechatPayMchPrivateKeyPath) appPrivateKey, err := utils.LoadPrivateKey(env.WechatPayMchPrivateKeyPath)
if err != nil { if err != nil {
panic(err) panic(err)
@@ -28,6 +32,7 @@ func InitWechatPay() {
panic(err) panic(err)
} }
// 创建 WechatPay 客户端
client, err := core.NewClient(context.Background(), client, err := core.NewClient(context.Background(),
option.WithWechatPayPublicKeyAuthCipher( option.WithWechatPayPublicKeyAuthCipher(
env.WechatPayMchId, env.WechatPayMchId,
@@ -41,7 +46,18 @@ func InitWechatPay() {
panic(err) panic(err)
} }
// 创建 WechatPay 通知处理器
handler, err := notify.NewRSANotifyHandler(env.WechatPayApiCert, verifiers.NewSHA256WithRSAPubkeyVerifier(
env.WechatPayPublicKeyId,
*wechatPublicKey,
))
if err != nil {
panic(err)
}
// 创建 WechatPay 服务
WechatPay = &WechatPayClient{ WechatPay = &WechatPayClient{
Native: &native.NativeApiService{Client: client}, Native: &native.NativeApiService{Client: client},
Notify: handler,
} }
} }

View File

@@ -1,19 +1,14 @@
package handlers package handlers
import ( import (
"platform/pkg/env"
"platform/pkg/u" "platform/pkg/u"
"platform/web/auth" "platform/web/auth"
"platform/web/common" "platform/web/common"
g "platform/web/globals"
q "platform/web/queries" q "platform/web/queries"
s "platform/web/services" s "platform/web/services"
"strconv"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/smartwalle/alipay/v3"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/native"
) )
// region ListResourcePss // region ListResourcePss
@@ -143,7 +138,7 @@ func AllResource(c *fiber.Ctx) error {
// endregion // endregion
// region CreateResourcePrepared // region CompleteResource
type CreateResourceReq struct { type CreateResourceReq struct {
s.CreateResourceData s.CreateResourceData
@@ -172,34 +167,21 @@ func PrepareResourceByAlipay(c *fiber.Ctx) error {
return err return err
} }
// 生成订单
tradeNo, err := s.ID.GenSerial(c.Context())
if err != nil {
return err
}
// 调用外部接口
alipayResp, err := g.Alipay.TradePagePay(alipay.TradePagePay{
QRPayMode: "4",
Trade: alipay.Trade{
OutTradeNo: tradeNo,
TotalAmount: strconv.FormatFloat(req.GetPrice(), 'f', 2, 64),
Subject: "购买套餐 - " + req.GetName(),
ProductCode: "FAST_INSTANT_TRADE_PAY",
TimeExpire: time.Now().Add(30 * time.Minute).Format("2006-01-02 15:04:05"),
},
})
if err != nil {
return err
}
// 保存交易信息 // 保存交易信息
err = s.Resource.PrepareResource(c.Context(), &req.CreateResourceData, authContext.Payload.Id, tradeNo, 1) result, err := s.Resource.PrepareResource(
c.Context(),
&req.CreateResourceData,
authContext.Payload.Id,
s.TransactionMethodAlipay,
)
if err != nil {
return err
}
// 返回结果 // 返回结果
return c.JSON(CreateResourceResp{ return c.JSON(CreateResourceResp{
TradeNo: tradeNo, TradeNo: result.TradeNo,
PayURL: alipayResp.String(), PayURL: result.PayURL,
}) })
} }
@@ -217,35 +199,21 @@ func PrepareResourceByWechat(c *fiber.Ctx) error {
return err return err
} }
// 生成订单号
tradeNo, err := s.ID.GenSerial(c.Context())
if err != nil {
return err
}
// 调用外部接口
wechatPayResp, _, err := g.WechatPay.Native.Prepay(c.Context(), native.PrepayRequest{
Mchid: &env.WechatPayMchId,
Appid: &env.WechatPayAppId,
Description: u.P("购买套餐 - " + req.GetName()),
OutTradeNo: &tradeNo,
TimeExpire: u.P(time.Now().Add(30 * time.Minute)),
NotifyUrl: &env.WechatPayCallbackUrl,
Amount: &native.Amount{
Total: u.P(int64(req.GetPrice() * 100)),
},
})
// 保存交易信息 // 保存交易信息
err = s.Resource.PrepareResource(c.Context(), &req.CreateResourceData, authContext.Payload.Id, tradeNo, 2) result, err := s.Resource.PrepareResource(
c.Context(),
&req.CreateResourceData,
authContext.Payload.Id,
s.TransactionMethodWeChat,
)
if err != nil { if err != nil {
return err return err
} }
// 返回结果 // 返回结果
return c.JSON(CreateResourceResp{ return c.JSON(CreateResourceResp{
TradeNo: tradeNo, TradeNo: result.TradeNo,
PayURL: *wechatPayResp.CodeUrl, PayURL: result.PayURL,
}) })
} }
@@ -263,28 +231,16 @@ func CreateResourceByAlipay(c *fiber.Ctx) error {
} }
// 验证支付结果 // 验证支付结果
alipayResp, err := g.Alipay.TradeQuery(c.Context(), alipay.TradeQuery{ result, err := s.Transaction.VerifyTransaction(c.Context(), &s.TransactionVerifyData{
OutTradeNo: req.TradeNo, TradeNo: req.TradeNo,
Method: s.TransactionMethodAlipay,
}) })
if err != nil { if err != nil {
return err return err
} }
if alipayResp.TradeStatus != "TRADE_SUCCESS" {
return fiber.NewError(fiber.StatusBadRequest, "支付未完成,请确认后重试")
}
// 创建套餐 // 完成套餐创建
payment, err := strconv.ParseFloat(alipayResp.ReceiptAmount, 64) err = s.Resource.CompleteResource(c.Context(), req.TradeNo, result)
if err != nil {
return err
}
paidAt, err := time.Parse("2006-01-02 15:04:05", alipayResp.SendPayDate)
if err != nil {
return err
}
err = s.Resource.CreateResourcePrepared(c.Context(), req.TradeNo, alipayResp.TradeNo, payment, paidAt)
if err != nil { if err != nil {
return err return err
} }
@@ -306,27 +262,16 @@ func CreateResourceByWechat(c *fiber.Ctx) error {
} }
// 验证支付结果 // 验证支付结果
wechatPayResp, _, err := g.WechatPay.Native.QueryOrderByOutTradeNo(c.Context(), native.QueryOrderByOutTradeNoRequest{ result, err := s.Transaction.VerifyTransaction(c.Context(), &s.TransactionVerifyData{
OutTradeNo: &req.TradeNo, TradeNo: req.TradeNo,
Mchid: &env.WechatPayMchId, Method: s.TransactionMethodWeChat,
}) })
if err != nil { if err != nil {
return err return err
} }
if *wechatPayResp.TradeState != "SUCCESS" { // 完成套餐创建
return fiber.NewError(fiber.StatusBadRequest, "支付未完成,请确认后重试") err = s.Resource.CompleteResource(c.Context(), req.TradeNo, result)
}
// 创建套餐
payment := float64(*wechatPayResp.Amount.PayerTotal) / 100
paidAt, err := time.Parse(time.RFC3339, *wechatPayResp.SuccessTime)
if err != nil {
return err
}
err = s.Resource.CreateResourcePrepared(c.Context(), req.TradeNo, *wechatPayResp.OutTradeNo, payment, paidAt)
if err != nil { if err != nil {
return err return err
} }
@@ -349,7 +294,7 @@ func CreateResourceByBalance(c *fiber.Ctx) error {
} }
// 创建套餐 // 创建套餐
err = s.Resource.CreateResourceImmediately(&req.CreateResourceData, authCtx.Payload.Id) err = s.Resource.CreateResource(&req.CreateResourceData, authCtx.Payload.Id)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,14 +1,19 @@
package handlers package handlers
import ( import (
"fmt"
"log/slog"
"net/http" "net/http"
g "platform/web/globals" g "platform/web/globals"
q "platform/web/queries"
s "platform/web/services" s "platform/web/services"
"strconv" "strconv"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/smartwalle/alipay/v3" "github.com/smartwalle/alipay/v3"
"github.com/valyala/fasthttp/fasthttpadaptor"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
) )
// region AlipayCallback // region AlipayCallback
@@ -16,73 +21,87 @@ import (
func AlipayCallback(c *fiber.Ctx) error { func AlipayCallback(c *fiber.Ctx) error {
// 解析请求 // 解析请求
req := make(map[string][]string) httpRequest := new(http.Request)
c.Context().QueryArgs().VisitAll(func(key, value []byte) { if err := fasthttpadaptor.ConvertRequest(c.Context(), httpRequest, false); err != nil {
req[string(key)] = append(req[string(key)], string(value)) return err
}) }
c.Context().PostArgs().VisitAll(func(key, value []byte) { if err := httpRequest.ParseForm(); err != nil {
req[string(key)] = append(req[string(key)], string(value)) return err
}) }
notification, err := g.Alipay.DecodeNotification(httpRequest.Form)
notification, err := g.Alipay.DecodeNotification(req)
if err != nil { if err != nil {
return err return err
} }
slog.Debug("支付宝支付回调", "notification", fmt.Sprintf("%+v", notification))
// todo 退款通知
if isRefund(notification) {
return act(c)
}
// 查询交易信息
trade, err := q.Q.Trade.Where(q.Trade.InnerNo.Eq(notification.OutTradeNo)).Take()
if err != nil {
// 跳过测试通知
return act(c)
}
switch notification.NotifyType { switch notification.NotifyType {
// 支付成功 // 支付成功
case string(alipay.TradeStatusSuccess): case string(alipay.TradeStatusSuccess):
if isRefund(notification) { // 收集交易状态
break
}
payment, err := strconv.ParseFloat(notification.TotalAmount, 64) payment, err := strconv.ParseFloat(notification.TotalAmount, 64)
if err != nil { if err != nil {
return err return err
} }
paidAt, err := time.Parse("2006-01-02 15:04:05", notification.GmtPayment) paidAt, err := time.Parse("2006-01-02 15:04:05", notification.GmtPayment)
if err != nil { if err != nil {
return err return err
} }
verified := &s.TransactionVerifyResult{
TransId: notification.TradeNo,
Payment: payment,
Time: paidAt,
}
switch trade.Type {
err = s.Resource.CreateResourcePrepared( // 余额充值
c.Context(), case 0:
notification.OutTradeNo, err := s.User.RechargeConfirm(c.Context(), notification.OutTradeNo, verified)
notification.TradeNo, if err != nil {
payment, return err
paidAt, }
)
if err != nil { // 购买产品
return err case 1:
err = s.Resource.CompleteResource(c.Context(), notification.OutTradeNo, verified)
if err != nil {
return err
}
} }
// 支付关闭 // 支付关闭
case string(alipay.TradeStatusClosed): case string(alipay.TradeStatusClosed):
switch trade.Type {
if isRefund(notification) { // 购买产品
break case 1:
cancelAt, err := time.Parse("2006-01-02 15:04:05", notification.GmtClose)
if err != nil {
return err
}
err = s.Resource.CancelResource(c.Context(), notification.OutTradeNo, cancelAt, s.TransactionMethodAlipay)
if err != nil {
return err
}
} }
cancelAt, err := time.Parse("2006-01-02 15:04:05", notification.GmtClose)
if err != nil {
return err
}
err = s.Resource.CancelResource(c.Context(), notification.OutTradeNo, cancelAt)
if err != nil {
return err
}
default:
} }
g.Alipay.ACKNotification(AdapterWriter{ return act(c)
c: c,
})
return nil
} }
type AdapterWriter struct { type AdapterWriter struct {
@@ -105,12 +124,72 @@ func isRefund(notification *alipay.Notification) bool {
return notification.OutBizNo != "" || notification.RefundFee != "" || notification.GmtRefund != "" return notification.OutBizNo != "" || notification.RefundFee != "" || notification.GmtRefund != ""
} }
func act(c *fiber.Ctx) error {
g.Alipay.ACKNotification(AdapterWriter{c: c})
return nil
}
// endregion // endregion
// region WechatPayCallback // region WechatPayCallback
func WechatPayCallback(c *fiber.Ctx) error { func WechatPayCallback(c *fiber.Ctx) error {
// 解析请求参数
req := new(http.Request)
if err := fasthttpadaptor.ConvertRequest(c.Context(), req, false); err != nil {
return err
}
content := new(payments.Transaction)
_, err := g.WechatPay.Notify.ParseNotifyRequest(c.Context(), req, content)
if err != nil {
return err
}
slog.Debug("微信支付回调", "content", fmt.Sprintf("%+v", content))
// 查询交易信息
trade, err := q.Q.Trade.Where(q.Trade.InnerNo.Eq(*content.OutTradeNo)).Take()
if err != nil {
// 跳过测试通知
return nil
}
switch *content.TradeState {
// 支付成功
case "SUCCESS":
// 收集交易状态
payment := float64(*content.Amount.PayerTotal) / 100
paidAt, err := time.Parse(time.RFC3339, *content.SuccessTime)
if err != nil {
return err
}
verified := &s.TransactionVerifyResult{
TransId: *content.TransactionId,
Payment: payment,
Time: paidAt,
}
switch {
// 余额充值
case trade.Type == 0:
err := s.User.RechargeConfirm(c.Context(), *content.OutTradeNo, verified)
if err != nil {
return err
}
// 购买产品
case trade.Type == 1:
err = s.Resource.CompleteResource(c.Context(), *content.OutTradeNo, verified)
if err != nil {
return err
}
}
}
return nil return nil
} }

View File

@@ -2,8 +2,11 @@ package handlers
import ( import (
"errors" "errors"
"platform/web/auth"
q "platform/web/queries" q "platform/web/queries"
"platform/web/services" s "platform/web/services"
"strconv"
"time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"gorm.io/gorm" "gorm.io/gorm"
@@ -25,7 +28,7 @@ func GetUserByToken(c *fiber.Ctx) error {
} }
// 查询会话信息 // 查询会话信息
session, err := services.Session.Find(c.Context(), req.Token) session, err := s.Session.Find(c.Context(), req.Token)
if err != nil { if err != nil {
return err return err
} }
@@ -47,3 +50,160 @@ func GetUserByToken(c *fiber.Ctx) error {
} }
// endregion // endregion
// region recharge
type RechargePrepareReq struct {
Amount float64 `json:"amount" validate:"required,min=0.01"`
}
type RechargePrepareResp struct {
TradeNo string `json:"trade_no"`
PayURL string `json:"pay_url"`
}
type RechargeConfirmReq struct {
TradeNo string `json:"trade_no" validate:"required"`
}
type RechargeConfirmResp struct {
Status string `json:"status"`
}
// RechargePrepareAlipay 通过支付宝充值
func RechargePrepareAlipay(c *fiber.Ctx) error {
// 检查权限
authContext, err := auth.Protect(c, []s.PayloadType{s.PayloadUser}, []string{})
if err != nil {
return err
}
// 解析请求参数
req := new(RechargePrepareReq)
if err := c.BodyParser(req); err != nil {
return err
}
// 保存交易信息
var result *s.TransactionPrepareResult
err = q.Q.Transaction(func(tx *q.Query) error {
result, err = s.Transaction.PrepareTransaction(c.Context(), tx, authContext.Payload.Id, &s.TransactionPrepareData{
Subject: "账户充值 - " + strconv.FormatFloat(req.Amount, 'f', 2, 64) + "元",
Amount: req.Amount,
ExpireAt: time.Now().Add(30 * time.Minute),
Type: s.TransactionTypeRecharge,
Method: s.TransactionMethodAlipay,
})
return err
})
if err != nil {
return err
}
// 返回结果
return c.JSON(RechargePrepareResp{
TradeNo: result.TradeNo,
PayURL: result.PayURL,
})
}
func RechargeConfirmAlipay(c *fiber.Ctx) error {
// 检查权限
_, err := auth.Protect(c, []s.PayloadType{s.PayloadUser}, []string{})
if err != nil {
return err
}
// 解析请求参数
req := new(RechargeConfirmReq)
if err := c.BodyParser(req); err != nil {
return err
}
// 验证支付结果
result, err := s.Transaction.VerifyTransaction(c.Context(), &s.TransactionVerifyData{
TradeNo: req.TradeNo,
Method: s.TransactionMethodAlipay,
})
if err != nil {
return err
}
// 更新数据库
err = s.User.RechargeConfirm(c.Context(), req.TradeNo, result)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success"})
}
func RechargePrepareWechat(c *fiber.Ctx) error {
// 检查权限
authContext, err := auth.Protect(c, []s.PayloadType{s.PayloadUser}, []string{})
if err != nil {
return err
}
// 解析请求参数
req := new(RechargePrepareReq)
if err := c.BodyParser(req); err != nil {
return err
}
// 保存交易信息
var result *s.TransactionPrepareResult
err = q.Q.Transaction(func(tx *q.Query) error {
result, err = s.Transaction.PrepareTransaction(c.Context(), tx, authContext.Payload.Id, &s.TransactionPrepareData{
Subject: "账户充值 - " + strconv.FormatFloat(req.Amount, 'f', 2, 64) + "元",
Amount: req.Amount,
ExpireAt: time.Now().Add(30 * time.Minute),
Type: s.TransactionTypeRecharge,
Method: s.TransactionMethodWeChat,
})
return err
})
if err != nil {
return err
}
// 返回结果
return c.JSON(RechargePrepareResp{
TradeNo: result.TradeNo,
PayURL: result.PayURL,
})
}
func RechargeConfirmWechat(c *fiber.Ctx) error {
// 检查权限
_, err := auth.Protect(c, []s.PayloadType{s.PayloadUser}, []string{})
if err != nil {
return err
}
// 解析请求参数
req := new(struct {
TradeNo string `json:"trade_no" validate:"required"`
})
if err := c.BodyParser(req); err != nil {
return err
}
// 验证支付结果
result, err := s.Transaction.VerifyTransaction(c.Context(), &s.TransactionVerifyData{
TradeNo: req.TradeNo,
})
if err != nil {
return err
}
// 更新数据库
err = s.User.RechargeConfirm(c.Context(), req.TradeNo, result)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success"})
}
// endregion

View File

@@ -30,6 +30,7 @@ type Trade struct {
Type int32 `gorm:"column:type;not null" json:"type"` Type int32 `gorm:"column:type;not null" json:"type"`
CancelAt common.LocalDateTime `gorm:"column:cancel_at" json:"cancel_at"` CancelAt common.LocalDateTime `gorm:"column:cancel_at" json:"cancel_at"`
PaidAt common.LocalDateTime `gorm:"column:paid_at" json:"paid_at"` PaidAt common.LocalDateTime `gorm:"column:paid_at" json:"paid_at"`
PayURL string `gorm:"column:pay_url" json:"pay_url"`
} }
// TableName Trade's table name // TableName Trade's table name

View File

@@ -43,6 +43,7 @@ func newTrade(db *gorm.DB, opts ...gen.DOOption) trade {
_trade.Type = field.NewInt32(tableName, "type") _trade.Type = field.NewInt32(tableName, "type")
_trade.CancelAt = field.NewField(tableName, "cancel_at") _trade.CancelAt = field.NewField(tableName, "cancel_at")
_trade.PaidAt = field.NewField(tableName, "paid_at") _trade.PaidAt = field.NewField(tableName, "paid_at")
_trade.PayURL = field.NewString(tableName, "pay_url")
_trade.fillFieldMap() _trade.fillFieldMap()
@@ -69,6 +70,7 @@ type trade struct {
Type field.Int32 Type field.Int32
CancelAt field.Field CancelAt field.Field
PaidAt field.Field PaidAt field.Field
PayURL field.String
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }
@@ -101,6 +103,7 @@ func (t *trade) updateTableName(table string) *trade {
t.Type = field.NewInt32(table, "type") t.Type = field.NewInt32(table, "type")
t.CancelAt = field.NewField(table, "cancel_at") t.CancelAt = field.NewField(table, "cancel_at")
t.PaidAt = field.NewField(table, "paid_at") t.PaidAt = field.NewField(table, "paid_at")
t.PayURL = field.NewString(table, "pay_url")
t.fillFieldMap() t.fillFieldMap()
@@ -117,7 +120,7 @@ func (t *trade) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
} }
func (t *trade) fillFieldMap() { func (t *trade) fillFieldMap() {
t.fieldMap = make(map[string]field.Expr, 16) t.fieldMap = make(map[string]field.Expr, 17)
t.fieldMap["id"] = t.ID t.fieldMap["id"] = t.ID
t.fieldMap["user_id"] = t.UserID t.fieldMap["user_id"] = t.UserID
t.fieldMap["inner_no"] = t.InnerNo t.fieldMap["inner_no"] = t.InnerNo
@@ -134,6 +137,7 @@ func (t *trade) fillFieldMap() {
t.fieldMap["type"] = t.Type t.fieldMap["type"] = t.Type
t.fieldMap["cancel_at"] = t.CancelAt t.fieldMap["cancel_at"] = t.CancelAt
t.fieldMap["paid_at"] = t.PaidAt t.fieldMap["paid_at"] = t.PaidAt
t.fieldMap["pay_url"] = t.PayURL
} }
func (t trade) clone(db *gorm.DB) trade { func (t trade) clone(db *gorm.DB) trade {

View File

@@ -43,6 +43,10 @@ func ApplyRouters(app *fiber.App) {
user.Post("/get/token", handlers.GetUserByToken) user.Post("/get/token", handlers.GetUserByToken)
user.Post("/identify", handlers.Identify) user.Post("/identify", handlers.Identify)
user.Post("/identify/callback", handlers.IdentifyCallback) user.Post("/identify/callback", handlers.IdentifyCallback)
user.Post("/recharge/prepare/alipay", handlers.RechargePrepareAlipay)
user.Post("/recharge/confirm/alipay", handlers.RechargeConfirmAlipay)
user.Post("/recharge/prepare/wechat", handlers.RechargePrepareWechat)
user.Post("/recharge/confirm/wechat", handlers.RechargeConfirmWechat)
// 账单 // 账单
bill := api.Group("/bill") bill := api.Group("/bill")

View File

@@ -18,56 +18,37 @@ var Resource = &resourceService{}
type resourceService struct{} type resourceService struct{}
func (s *resourceService) PrepareResource(ctx context.Context, data *CreateResourceData, uid int32, tradeNo string, method int32) error { func (s *resourceService) PrepareResource(ctx context.Context, data *CreateResourceData, uid int32, method TransactionMethod) (*TransactionPrepareResult, error) {
amount := data.GetPrice() amount := data.GetPrice()
// 保存到数据库 // 保存到数据库
var result *TransactionPrepareResult
err := q.Q.Transaction(func(q *q.Query) error { err := q.Q.Transaction(func(q *q.Query) error {
// 创建交易订单 var err error
var trade = m.Trade{
UserID: uid,
InnerNo: tradeNo,
Subject: "购买套餐 - " + data.GetName(),
Method: method,
Type: 1,
Status: 0,
Amount: amount,
}
err := q.Trade.Create(&trade)
if err != nil {
return err
}
// 保存用户帐 // 生成交易订
bill := m.Bill{ result, err = Transaction.PrepareTransaction(ctx, q, uid, &TransactionPrepareData{
UserID: uid, Subject: "购买套餐 - " + data.GetName(),
TradeID: trade.ID, Amount: amount,
BillNo: ID.GenReadable("bil"), ExpireAt: time.Now().Add(30 * time.Minute),
Info: "购买套餐 - " + data.GetName(), Type: TransactionTypePurchase,
Type: 1, Method: method,
Amount: -amount, })
}
err = q.Bill.
Omit(q.Bill.ResourceID, q.Bill.RefundID).
Create(&bill)
if err != nil { if err != nil {
return err return err
} }
// 保存请求缓存 // 保存请求缓存
cache := &CreateResourceCache{ reqStr, err := json.Marshal(&CreateResourceCache{
CreateResourceData: *data, CreateResourceData: *data,
Uid: uid, Uid: uid,
TradeId: trade.ID, TradeId: result.Trade.ID,
BillId: bill.ID, BillId: result.Bill.ID,
} })
reqStr, err := json.Marshal(cache)
if err != nil { if err != nil {
return err return err
} }
err = rds.Client.Set(ctx, result.TradeNo, reqStr, 30*time.Minute).Err()
err = rds.Client.Set(ctx, tradeNo, reqStr, 30*time.Minute).Err()
if err != nil { if err != nil {
return err return err
} }
@@ -75,20 +56,19 @@ func (s *resourceService) PrepareResource(ctx context.Context, data *CreateResou
return nil return nil
}) })
if err != nil { if err != nil {
return err return nil, err
} }
return nil return result, nil
} }
func (s *resourceService) CreateResourcePrepared(ctx context.Context, tradeNo string, outerTradeNo string, payment float64, at time.Time) error { func (s *resourceService) CompleteResource(ctx context.Context, tradeNo string, rs *TransactionVerifyResult) error {
// 获取请求缓存 // 获取请求缓存
reqStr, err := rds.Client.Get(ctx, tradeNo).Result() reqStr, err := rds.Client.Get(ctx, tradeNo).Result()
if err != nil { if err != nil {
return err return err
} }
cache := new(CreateResourceCache) cache := new(CreateResourceCache)
if err := json.Unmarshal([]byte(reqStr), cache); err != nil { if err := json.Unmarshal([]byte(reqStr), cache); err != nil {
return err return err
@@ -103,21 +83,7 @@ func (s *resourceService) CreateResourcePrepared(ctx context.Context, tradeNo st
return err return err
} }
// 更新订单状态 // 更新账单
_, err = q.Trade.Debug().
Select(q.Trade.OuterNo, q.Trade.Payment, q.Trade.Status, q.Trade.PaidAt).
Updates(&m.Trade{
ID: cache.TradeId,
OuterNo: outerTradeNo,
Payment: payment,
Status: 1,
PaidAt: common.LocalDateTime(at),
})
if err != nil {
return err
}
// 更新账单状态
_, err = q.Bill.Debug(). _, err = q.Bill.Debug().
Select(q.Bill.ResourceID). Select(q.Bill.ResourceID).
Updates(&m.Bill{ Updates(&m.Bill{
@@ -128,6 +94,15 @@ func (s *resourceService) CreateResourcePrepared(ctx context.Context, tradeNo st
return err return err
} }
// 完成交易
_, err = Transaction.CompleteTransaction(ctx, q, &TransactionCompleteData{
TradeNo: tradeNo,
TransactionVerifyResult: *rs,
})
if err != nil {
return err
}
// 删除缓存 // 删除缓存
err = rds.Client.Del(ctx, tradeNo).Err() err = rds.Client.Del(ctx, tradeNo).Err()
if err != nil { if err != nil {
@@ -143,10 +118,24 @@ func (s *resourceService) CreateResourcePrepared(ctx context.Context, tradeNo st
return nil return nil
} }
func (s *resourceService) CreateResourceImmediately(data *CreateResourceData, uid int32) error { func (s *resourceService) CreateResource(data *CreateResourceData, uid int32) error {
// 保存交易信息 // 保存交易信息
err := q.Q.Transaction(func(q *q.Query) error { err := q.Q.Transaction(func(q *q.Query) error {
amount := data.GetPrice()
// 检查用户
user, err := q.User.
Where(q.User.ID.Eq(uid)).
Take()
if err != nil {
return err
}
// 检查余额
if user.Balance < amount {
return fiber.NewError(fiber.StatusBadRequest, "余额不足")
}
// 保存套餐 // 保存套餐
resource, err := createResource(data, uid) resource, err := createResource(data, uid)
@@ -170,6 +159,14 @@ func (s *resourceService) CreateResourceImmediately(data *CreateResourceData, ui
return err return err
} }
// 更新用户余额
_, err = q.User.
Where(q.User.ID.Eq(uid)).
Update(q.User.Balance, user.Balance-amount)
if err != nil {
return err
}
return nil return nil
}) })
if err != nil { if err != nil {
@@ -221,27 +218,10 @@ type CreateResourceCache struct {
} }
func createResource(data *CreateResourceData, uid int32) (*m.Resource, error) { func createResource(data *CreateResourceData, uid int32) (*m.Resource, error) {
amount := data.GetPrice()
// 检查用户
user, err := q.User.
Where(
q.User.ID.Eq(uid),
q.User.Status.Eq(1),
).
Take()
if err != nil {
return nil, err
}
// 检查余额
if user.Balance < amount {
return nil, fiber.NewError(fiber.StatusBadRequest, "余额不足")
}
// 创建套餐 // 创建套餐
resource := &m.Resource{ resource := m.Resource{
UserID: user.ID, UserID: uid,
ResourceNo: ID.GenReadable("res"), ResourceNo: ID.GenReadable("res"),
Active: true, Active: true,
Type: 1, Type: 1,
@@ -253,38 +233,29 @@ func createResource(data *CreateResourceData, uid int32) (*m.Resource, error) {
DailyLimit: data.DailyLimit, DailyLimit: data.DailyLimit,
}, },
} }
err = q.Resource.Create(resource) err := q.Resource.Create(&resource)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 更新用户余额 return &resource, nil
user.Balance -= amount
_, err = q.User.
Where(q.User.ID.Eq(uid)).
Update(q.User.Balance, user.Balance)
if err != nil {
return nil, err
}
return resource, nil
} }
func (s *resourceService) CancelResource(ctx context.Context, tradeNo string, at time.Time) error { func (s *resourceService) CancelResource(ctx context.Context, tradeNo string, at time.Time, method TransactionMethod) error {
// 获取请求缓存 // 删除请求缓存
_, err := rds.Client.Del(ctx, tradeNo).Result() _, err := rds.Client.Del(ctx, tradeNo).Result()
if err != nil { if err != nil {
return err return err
} }
// 取消交易
err = Transaction.RevokeTransaction(ctx, tradeNo, method)
if err != nil {
return err
}
// 更新订单状态 // 更新订单状态
_, err = q.Trade. err = Transaction.FinishTransaction(ctx, q.Q, tradeNo, at)
Where(q.Trade.InnerNo.Eq(tradeNo)).
Select(q.Trade.Status, q.Trade.CancelAt).
Updates(m.Trade{
Status: 2,
CancelAt: common.LocalDateTime(at),
})
if err != nil { if err != nil {
return err return err
} }

324
web/services/transaction.go Normal file
View File

@@ -0,0 +1,324 @@
package services
import (
"context"
"errors"
"io"
"log/slog"
"net/http"
"platform/pkg/env"
"platform/pkg/u"
"platform/web/common"
g "platform/web/globals"
m "platform/web/models"
q "platform/web/queries"
"strconv"
"time"
"github.com/smartwalle/alipay/v3"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/native"
)
var Transaction = &transactionService{}
type transactionService struct {
}
func (s *transactionService) PrepareTransaction(ctx context.Context, q *q.Query, uid int32, data *TransactionPrepareData) (*TransactionPrepareResult, error) {
var subject = data.Subject
var amount = data.Amount
var expire = data.ExpireAt
var tType = data.Type
var method = data.Method
// 生成订单号
tradeNo, err := ID.GenSerial(ctx)
if err != nil {
return nil, err
}
// 创建支付订单
var payUrl string
switch method {
// 调用支付宝支付接口
case TransactionMethodAlipay:
resp, err := g.Alipay.TradePagePay(alipay.TradePagePay{
QRPayMode: "4",
Trade: alipay.Trade{
ProductCode: "FAST_INSTANT_TRADE_PAY",
OutTradeNo: tradeNo,
Subject: subject,
TotalAmount: strconv.FormatFloat(amount, 'f', 2, 64),
TimeExpire: expire.Format("2006-01-02 15:04:05"),
},
})
if err != nil {
return nil, err
}
payUrl = resp.String()
// 调用微信支付接口
case TransactionMethodWeChat:
resp, _, err := g.WechatPay.Native.Prepay(ctx, native.PrepayRequest{
Appid: &env.WechatPayAppId,
Mchid: &env.WechatPayMchId,
OutTradeNo: &tradeNo,
Description: &subject,
TimeExpire: &expire,
NotifyUrl: &env.WechatPayCallbackUrl,
Amount: &native.Amount{
Total: u.P(int64(amount * 100)),
},
})
if err != nil {
return nil, err
}
payUrl = *resp.CodeUrl
// 不支持的支付方式
default:
return nil, errors.New("不支持的支付方式")
}
// 保存交易订单
var trade = m.Trade{
UserID: uid,
InnerNo: tradeNo,
Subject: subject,
Method: int32(method),
Type: int32(tType),
Amount: amount,
Status: 0, // 0-待支付
PayURL: payUrl,
}
err = q.Trade.Create(&trade)
if err != nil {
return nil, err
}
// 保存用户帐单
var bill = m.Bill{
BillNo: ID.GenReadable("bil"),
UserID: uid,
TradeID: trade.ID,
Info: subject,
Type: int32(tType),
Amount: amount,
}
err = q.Bill.
Omit(q.Bill.ResourceID, q.Bill.RefundID).
Create(&bill)
if err != nil {
return nil, err
}
return &TransactionPrepareResult{
TradeNo: tradeNo,
PayURL: payUrl,
Bill: &bill,
Trade: &trade,
}, nil
}
func (s *transactionService) VerifyTransaction(ctx context.Context, data *TransactionVerifyData) (*TransactionVerifyResult, error) {
var tradeNo = data.TradeNo
var method = data.Method
// 检查交易号是否存在
var transId string
var paidAt time.Time
var payment float64
switch method {
// 检查支付宝交易
case TransactionMethodAlipay:
resp, err := g.Alipay.TradeQuery(ctx, alipay.TradeQuery{
OutTradeNo: tradeNo,
})
if err != nil {
return nil, err
}
if resp.Code != alipay.CodeSuccess {
slog.Warn("支付宝交易取消失败", "code", resp.Code, "sub_code", resp.SubCode, "msg", resp.Msg)
return nil, errors.New("交易查询失败")
}
if resp.TradeStatus != alipay.TradeStatusSuccess {
return nil, errors.New("交易未完成")
}
transId = resp.TradeNo
payment, err = strconv.ParseFloat(resp.TotalAmount, 64)
if err != nil {
return nil, err
}
paidAt, err = time.Parse("2006-01-02 15:04:05", resp.SendPayDate)
if err != nil {
return nil, err
}
// 检查微信交易
case TransactionMethodWeChat:
resp, _, err := g.WechatPay.Native.QueryOrderByOutTradeNo(ctx, native.QueryOrderByOutTradeNoRequest{
OutTradeNo: &tradeNo,
Mchid: &env.WechatPayMchId,
})
if err != nil {
return nil, err
}
if *resp.TradeState != "SUCCESS" {
return nil, errors.New("交易未完成")
}
transId = *resp.TransactionId
payment = float64(*resp.Amount.PayerTotal) / 100
paidAt, err = time.Parse(time.RFC3339, *resp.SuccessTime)
if err != nil {
return nil, err
}
// 不支持的支付方式
default:
return nil, errors.New("不支持的支付方式")
}
return &TransactionVerifyResult{
TransId: transId,
Payment: payment,
Time: paidAt,
}, nil
}
func (s *transactionService) CompleteTransaction(ctx context.Context, q *q.Query, data *TransactionCompleteData) (*TransactionCompleteResult, error) {
var transId = data.TransId
var tradeNo = data.TradeNo
var payment = data.Payment
var paidAt = data.Time
// 获取交易信息
trade, err := q.Trade.WithContext(ctx).
Where(q.Trade.InnerNo.Eq(tradeNo)).
First()
if err != nil {
return nil, err
}
// 检查交易状态
if trade.Status != 0 {
return nil, nil
}
// 更新交易状态
trade.Status = 1
trade.OuterNo = transId
trade.Payment = payment
trade.PaidAt = common.LocalDateTime(paidAt)
trade.PayURL = ""
_, err = q.Trade.WithContext(ctx).Updates(trade)
if err != nil {
return nil, err
}
return &TransactionCompleteResult{
Trade: trade,
}, nil
}
func (s *transactionService) RevokeTransaction(ctx context.Context, tradeNo string, method TransactionMethod) error {
switch method {
case TransactionMethodAlipay:
resp, err := g.Alipay.TradeCancel(ctx, alipay.TradeCancel{
OutTradeNo: tradeNo,
})
if err != nil {
return err
}
if resp.Code != alipay.CodeSuccess {
slog.Warn("支付宝交易取消失败", "code", resp.Code, "sub_code", resp.SubCode, "msg", resp.Msg)
return errors.New("交易取消失败")
}
case TransactionMethodWeChat:
resp, err := g.WechatPay.Native.CloseOrder(ctx, native.CloseOrderRequest{
Mchid: &env.WechatPayMchId,
OutTradeNo: &tradeNo,
})
if err != nil {
return err
}
if resp.Response.StatusCode != http.StatusNoContent {
body, _ := io.ReadAll(resp.Response.Body)
slog.Warn("微信交易取消失败", "code", resp.Response.StatusCode, "body", string(body))
return errors.New("交易取消失败")
}
}
return nil
}
func (s *transactionService) FinishTransaction(ctx context.Context, q *q.Query, tradeNo string, time time.Time) error {
_, err := q.Trade.
Where(q.Trade.InnerNo.Eq(tradeNo)).
Select(q.Trade.Status, q.Trade.CancelAt, q.Trade.PayURL).
Updates(m.Trade{
Status: 2,
CancelAt: common.LocalDateTime(time),
PayURL: "",
})
if err != nil {
return err
}
return nil
}
type TransactionType int32
const (
TransactionTypeRecharge TransactionType = iota
TransactionTypePurchase
)
type TransactionMethod int32
const (
TransactionMethodAlipay TransactionMethod = iota
TransactionMethodWeChat
)
type TransactionPrepareData struct {
Subject string
Amount float64
ExpireAt time.Time
Type TransactionType
Method TransactionMethod
}
type TransactionPrepareResult struct {
TradeNo string
PayURL string
Bill *m.Bill
Trade *m.Trade
}
type TransactionVerifyData struct {
TradeNo string
Method TransactionMethod
}
type TransactionVerifyResult struct {
TransId string
Payment float64
Time time.Time
}
type TransactionCompleteData struct {
TradeNo string
TransactionVerifyResult
}
type TransactionCompleteResult struct {
Trade *m.Trade
}

46
web/services/user.go Normal file
View File

@@ -0,0 +1,46 @@
package services
import (
"context"
q "platform/web/queries"
)
var User = &userService{}
type userService struct{}
func (s *userService) RechargeConfirm(ctx context.Context, tradeNo string, verified *TransactionVerifyResult) error {
err := q.Q.Transaction(func(tx *q.Query) error {
// 更新交易状态
result, err := Transaction.CompleteTransaction(ctx, tx, &TransactionCompleteData{
TradeNo: tradeNo,
TransactionVerifyResult: *verified,
})
if err != nil {
return err
}
// 更新用户余额
user, err := tx.User.
Where(tx.User.ID.Eq(result.Trade.UserID)).Take()
if err != nil {
return err
}
_, err = tx.User.
Where(tx.User.ID.Eq(user.ID)).
Update(tx.User.Balance, user.Balance+result.Trade.Amount)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return nil
}