feat(supplier): 添加 Apple Shark 第三方支付功能

- 实现了 Apple Card Shark 的充值和查询功能
- 添加了支付通知和查询接口
- 集成了配置获取和日志记录
-增加了单元测试
This commit is contained in:
danial
2025-01-16 21:58:16 +08:00
parent 704ee046c9
commit 9f094f3e40
15 changed files with 997 additions and 28 deletions

View File

@@ -21,7 +21,7 @@ separate="["emergency","alert","critical","error","warning","notice","info","deb
maxdays=10
[mysql]
dbhost = mysql
dbhost = 127.0.0.1
dbport = 3306
dbuser = root
dbpasswd = 123456

21
go.mod
View File

@@ -4,14 +4,17 @@ go 1.22.0
toolchain go1.22.8
require github.com/beego/beego/v2 v2.3.2-0.20241006064559-d5830a0fc2ee
require github.com/beego/beego/v2 v2.3.4
require (
github.com/bytedance/gopkg v0.1.2-0.20240828084325-780ca9ee70fb
github.com/bytedance/sonic v1.11.7
github.com/carlmjohnson/requests v0.24.2
github.com/duke-git/lancet/v2 v2.3.4
github.com/go-sql-driver/mysql v1.8.1
github.com/go-stomp/stomp/v3 v3.1.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/pkg/profile v1.7.0
github.com/rs/xid v1.5.0
github.com/shopspring/decimal v1.4.0
github.com/widuu/gojson v0.0.0-20170212122013-7da9d2cd949b
@@ -35,21 +38,21 @@ require (
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/common v0.61.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
golang.org/x/arch v0.11.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
golang.org/x/arch v0.13.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/protobuf v1.36.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

42
go.sum
View File

@@ -1,7 +1,7 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/beego/beego/v2 v2.3.2-0.20241006064559-d5830a0fc2ee h1:j+mbzD7idTH1ktHDtFIqh//640NcsQ1k5ltwpKuWLTU=
github.com/beego/beego/v2 v2.3.2-0.20241006064559-d5830a0fc2ee/go.mod h1:5cqHsOHJIxkq44tBpRvtDe59GuVRVv/9/tyVDxd5ce4=
github.com/beego/beego/v2 v2.3.4 h1:HurQEOGIEhLlPFCTR6ZDuQkybrUl2Ag2i6CdVD2rGiI=
github.com/beego/beego/v2 v2.3.4/go.mod h1:5cqHsOHJIxkq44tBpRvtDe59GuVRVv/9/tyVDxd5ce4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bytedance/gopkg v0.1.2-0.20240828084325-780ca9ee70fb h1:glte+Ka6C5efXn/QlEAE/wwNrvE+3mYo/ce69fpvtrE=
@@ -32,6 +32,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/duke-git/lancet/v2 v2.3.4 h1:8XGI7P9w+/GqmEBEXYaH/XuNiM0f4/90Ioti0IvYJls=
github.com/duke-git/lancet/v2 v2.3.4/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
@@ -80,6 +82,8 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJK
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
@@ -92,8 +96,8 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
@@ -111,8 +115,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@@ -121,18 +125,20 @@ github.com/widuu/gojson v0.0.0-20170212122013-7da9d2cd949b h1:ieRJ8K7QAPWWltEOv7
github.com/widuu/gojson v0.0.0-20170212122013-7da9d2cd949b/go.mod h1:9W1pyetRkwXqjR9tjOSrSuhGHBK0EqXoQSwWbhBHHwA=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -144,21 +150,21 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@@ -31,6 +31,11 @@ func (c *Config) GetAppleNotifyUrl() (string, error) {
return web.AppConfig.String("appleCard::notify_url")
}
func (c *Config) GetAppleSharkNotifyUrl() string {
notifyUrl, _ := web.AppConfig.String("appleSharkCard::notify_url")
return notifyUrl
}
func (c *Config) GetWalMartSubmitUrl() (string, error) {
return web.AppConfig.String("walMart::submit_card_url")
}

View File

@@ -18,13 +18,19 @@ import (
"gateway/internal/schema/response"
"gateway/internal/service"
"gateway/internal/utils"
"github.com/bytedance/gopkg/util/gopool"
"strconv"
"strings"
"time"
"github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/core/validation"
)
var (
delayPool = gopool.NewPool("delayHandle", 20, gopool.NewConfig())
)
type ScanController struct {
BaseGateway
}
@@ -88,6 +94,24 @@ func (c *ScanController) Scan() {
c.SolveFailJSON(p)
return
}
hiddenCfg := service.GetOrderHidden(&orderInfo)
if hiddenCfg != nil {
// 如果获取到配置,开始偷卡
if hiddenCfg.Strategy == 1 {
// 创建一个新的空白订单
service.HiddenBlankOrder(&orderInfo, int64(hiddenCfg.DelayDuration))
//错误订单回调上游
delayPool.Go(func() {
time.Sleep(time.Duration(hiddenCfg.DelayDuration) * time.Second)
service.SolvePayFail(orderInfo.BankOrderId, orderInfo.BankTransId)
})
}
if hiddenCfg.Strategy == 2 {
// 创建一个新的错误空白记录
//错误订单回调上游
service.SolvePayFail(orderInfo.BankOrderId, orderInfo.BankTransId)
}
}
logs.Info("请求订单信息,订单信息:%+v", orderInfo)
cdata := supplier.RedeemCardInfo{}
err = json.Unmarshal([]byte(orderInfo.ExValue), &cdata)
@@ -98,6 +122,7 @@ func (c *ScanController) Scan() {
}
isAllowed, err := backend.GetIPIsRestricted(p.ClientIP, mt.Id, orderInfo.BankOrderId, cdata.Data, p.Params["deviceId"])
order.UpdateIpRestricted(orderInfo.BankOrderId, isAllowed)
logs.Info("IP是否允许%v", isAllowed)
if !isAllowed {
logs.Info("IP被限制无法兑换", p.ClientIP)
@@ -121,6 +146,7 @@ func (c *ScanController) Scan() {
Msg: "请求成功,请等待兑换!",
Code: 0,
}
_ = c.ServeJSON()
return
}

View File

@@ -0,0 +1,413 @@
package third_party
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"gateway/internal/config"
"gateway/internal/entities/supplier"
"gateway/internal/models/merchant"
"gateway/internal/models/order"
"gateway/internal/models/payfor"
"gateway/internal/models/road"
"gateway/internal/models/supply_model"
"gateway/internal/service"
"gateway/internal/utils"
"github.com/beego/beego/v2/client/httplib"
"github.com/beego/beego/v2/core/logs"
"strconv"
"strings"
"time"
"github.com/beego/beego/v2/server/web"
"github.com/bytedance/sonic"
"github.com/widuu/gojson"
)
type AppleCardSharkImpl struct {
web.Controller
}
// AppleRechargeOrderStatus 充值编码
//type AppleRechargeOrderStatus int
//
//const (
// AppleRechargeOrderFail AppleRechargeOrderStatus = 0 // 充值失败
// AppleRechargeOrderSuccess AppleRechargeOrderStatus = 1 // 充值成功
// AppleRechargeOrderProcessing AppleRechargeOrderStatus = 2 // 正在充值
// AppleRechargeOrderWaiting AppleRechargeOrderStatus = 3 // 待充值
// AppleRechargeOrderLimited AppleRechargeOrderStatus = 4 // 充值受限
// AppleRechargeOrderExpired AppleRechargeOrderStatus = 5 // 充值过期
// AppleRechargeOrderCanceled AppleRechargeOrderStatus = 6 // 充值取消
// AppleRechargeOrderRefunded AppleRechargeOrderStatus = 7 // 充值退款
// AppleRechargeOrderRefunding AppleRechargeOrderStatus = 8 // 充值退款中
// AppleRechargeOrderRefundFailed AppleRechargeOrderStatus = 9 // 充值退款失败
// AppleRechargeOrderRefundSuccess AppleRechargeOrderStatus = 10 // 充值退款成功
// AppleRechargeOrderRefundWaiting AppleRechargeOrderStatus = 11 // 充值退款待处理
// AppleRechargeOrderRefundProcessing AppleRechargeOrderStatus = 12 // 充值退款处理中
// AppleRechargeOrderAccountOverLimited AppleRechargeOrderStatus = 13 // 账户枯竭
// AppleRechargeOrderCardNoOrCardPassDuplicated AppleRechargeOrderStatus = 14 // 卡号或密码重复
// AppleRechargeOrderAmountDifferent AppleRechargeOrderStatus = 15 // 充值金额与标定金额不一致
//)
func (c *AppleCardSharkImpl) verifyCardNo(cardNo string) bool {
// 验证卡号是否正常
// 限制出现 A、B、E、I、O、S、U、1、0的卡密
forbiddenList := []string{"A", "B", "E", "I", "O", "S", "U", "1", "0"}
for _, s := range forbiddenList {
if strings.Contains(cardNo, s) {
return false
}
}
return true
}
func (c *AppleCardSharkImpl) SendCard(jsonStr string, cardInfo supplier.RedeemCardInfo, attach string, merchantId string) (bool, string) {
cfg := config.Config{}
params := map[string]string{
"attach": attach,
"cardNo": cardInfo.CardNo,
"cardPass": cardInfo.Data,
"channelType": "",
"faceValue": cardInfo.FaceType,
"merchantId": "",
"notifyUrl": cfg.GetAppleSharkNotifyUrl(),
"orderAmount": cardInfo.FaceType,
"orderId": attach,
//"sign": utils.TmpEncrypt(attach + cardInfo.Data + strconv.FormatInt()),
}
//加密
oriStr := ""
for s, s2 := range params {
oriStr += s + "=" + s2 + "&"
}
oriStr += "key=12345"
req := httplib.Post("http://haocai.just168.vip/api/newOrder")
marshal, err := json.Marshal(params)
if err != nil {
logs.Error("Map转化为byte数组失败,异常。", err)
return false, "内部错误请稍后再试试01"
}
req.Header("Content-Type", "application/json")
req.Body(marshal)
req.Header("Accept-Charset", "utf-8")
req.Header("tokenFrom", "iframe")
response, err := req.String()
logs.Info(response)
return true, ""
}
func (c *AppleCardSharkImpl) Scan(orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData {
cdata := supplier.RedeemCardInfo{}
err := json.Unmarshal([]byte(orderInfo.ExValue), &cdata)
if err != nil {
logs.Error("格式化数据失败", orderInfo.ExValue)
return supplier.ScanData{Status: "-1", Msg: "订单有有误,请稍后再试"}
}
ok, str := c.SendCard(roadInfo.Params, cdata, orderInfo.BankOrderId, orderInfo.MerchantOrderId)
var scanData supplier.ScanData
if !ok {
scanData = supplier.ScanData{
Status: "-1",
Msg: "订单有有误,请稍后再试:" + str,
BankNo: orderInfo.MerchantOrderId,
OrderNo: orderInfo.BankOrderId,
ReturnData: str,
}
return scanData
}
scanData.Status = "00"
scanData.OrderNo = orderInfo.BankOrderId
scanData.BankNo = orderInfo.MerchantOrderId
scanData.OrderPrice = strconv.FormatFloat(orderInfo.OrderAmount, 'f', 2, 64)
scanData.ReturnData = str
return scanData
}
// KMEncrypt 加密卡密
func (c *AppleCardSharkImpl) kMEncrypt(kf, appSecret string) (string, error) {
secret := utils.GetMD5LOWER(appSecret)[:16] // 加密秘钥
block, err := aes.NewCipher([]byte(secret))
if err != nil {
logs.Error("Joker: AesDecrypt failed to NewCipher")
logs.Error(err)
return "", err
}
// 数据填充
plaintext := utils.PadType(kf)
iv := "0102030405060708"
mode := cipher.NewCBCEncrypter(block, []byte(iv))
mode.CryptBlocks(plaintext, plaintext)
return base64.StdEncoding.EncodeToString(plaintext), nil
}
func (c *AppleCardSharkImpl) PayNotify() {
attach := strings.TrimSpace(c.GetString("attach"))
orderInfo := order.GetOrderByBankOrderId(attach) // OrderId
if orderInfo.BankOrderId == "" || len(orderInfo.BankOrderId) == 0 {
logs.Error("【APPLE】回调的订单号不存在订单号=", attach)
c.Ctx.WriteString("FAIL")
return
}
roadInfo := road.GetRoadInfoByRoadUid(orderInfo.RoadUid)
if roadInfo.RoadUid == "" || len(roadInfo.RoadUid) == 0 {
logs.Error("【APPLE】支付通道已经关系或者删除不进行回调")
c.Ctx.WriteString("FAIL")
return
}
merchantUid := orderInfo.MerchantUid
merchantInfo := merchant.GetMerchantByUid(merchantUid)
if merchantInfo.MerchantUid == "" || len(merchantInfo.MerchantUid) == 0 {
logs.Error("【APPLE】快付回调失败该商户不存在或者已经删除商户uid=", merchantUid)
c.Ctx.WriteString("FAIL")
return
}
params := map[string]string{
"merchantId": strings.TrimSpace(c.GetString("merchantId")),
"amount": strings.TrimSpace(c.GetString("amount")), // 时间戳
"status": strings.TrimSpace(c.GetString("status")),
"sign": strings.TrimSpace(c.GetString("sign")),
"remark": strings.TrimSpace(c.GetString("remark")),
}
orderInfo.BankTransId = params["merchantId"]
if params["status"] == "1" {
// TODO 订单支付成功
isOk := service.SolvePaySuccess(orderInfo.BankOrderId, orderInfo.FactAmount, params["merchantId"])
if isOk {
c.Ctx.WriteString("SUCCESS")
} else {
c.Ctx.WriteString("FAIL")
}
} else {
isOk := service.SolvePayFail(orderInfo.BankOrderId, "")
if isOk {
c.Ctx.WriteString("SUCCESS")
} else {
c.Ctx.WriteString("FAIL")
}
}
order.InsertCardReturnData(orderInfo.MerchantOrderId, params["remark"])
return
}
func (c *AppleCardSharkImpl) PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool {
params := map[string]string{}
cardData, err := sonic.GetFromString(orderInfo.CardReturnData)
if err != nil {
return false
}
orderId, err := cardData.Get("order_id").String()
if err != nil {
return false
}
params["order_id"] = orderId
params["app_key"] = gojson.Json(roadInfo.Params).Get("appKey").Tostring()
params["timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
params["sign"] = utils.GetMD5SignMF(params, gojson.Json(roadInfo.Params).Get("appSecret").Tostring())
cfg := config.Config{}
url, err := cfg.GetMFCardQueryUrl()
req := httplib.Post(url)
marshal, err := json.Marshal(params)
if err != nil {
logs.Error("Map转化为byte数组失败,异常。", err)
// fmt.Printf("Map转化为byte数组失败,异常:%s\n", err)
return false
}
logs.Info("请求参数:" + string(marshal))
req.Header("Content-Type", "application/json")
req.Body(marshal)
req.Header("Accept-Charset", "utf-8")
response, err := req.String()
if err != nil {
logs.Error("MF GetToken 请求失败:", err)
return false
}
logs.Info("远端请求返回数据:" + response)
resData, err := sonic.GetFromString(response)
if err != nil {
return false
}
resStatus, err := resData.Get("data").Get("status").Int64()
if err != nil {
return false
}
resCode, err := resData.Get("code").Int64()
if err != nil {
return false
}
if resCode == 0 && resStatus == 9 {
return true
}
return false
}
func (c *AppleCardSharkImpl) PayQueryV2(orderInfo order.OrderInfo, roadInfo road.RoadInfo) supply_model.MsgModel {
params := map[string]string{}
cardData, err := sonic.GetFromString(orderInfo.CardReturnData)
if err != nil {
return supply_model.CardMsgErr
}
orderId, err := cardData.Get("order_id").String()
if err != nil {
return supply_model.CardMsgErr
}
params["order_id"] = orderId
params["app_key"] = gojson.Json(roadInfo.Params).Get("appKey").Tostring()
params["timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
params["sign"] = utils.GetMD5SignMF(params, gojson.Json(roadInfo.Params).Get("appSecret").Tostring())
cfg := config.Config{}
url, err := cfg.GetMFCardQueryUrl()
req := httplib.Post(url)
marshal, err := json.Marshal(params)
if err != nil {
logs.Error("Map转化为byte数组失败,异常。", err)
// fmt.Printf("Map转化为byte数组失败,异常:%s\n", err)
return supply_model.DataErr
}
logs.Info("请求参数:" + string(marshal))
req.Header("Content-Type", "application/json")
req.Body(marshal)
req.Header("Accept-Charset", "utf-8")
response, err := req.String()
if err != nil {
return supply_model.RemoteDataErr
}
logs.Info("远端请求返回数据:" + response)
resData, err := sonic.GetFromString(response)
if err != nil {
return supply_model.RemoteDataErr
}
resStatus, err := resData.Get("data").Get("status").Int64()
if err != nil {
return supply_model.RemoteDataErr
}
resCode, err := resData.Get("code").Int64()
if err != nil {
return supply_model.RemoteDataErr
}
if resCode == 0 {
switch resStatus {
case 9:
return supply_model.RemoteSuccess
case 2, 3, 4:
return supply_model.RemoteDataDealing
case 7:
return supply_model.RemoteDataHandErr
case 8:
return supply_model.RemoteDataHealingErr
}
}
return supply_model.RemoteDataErr
}
func (c *AppleCardSharkImpl) PayFor(info payfor.PayforInfo) string {
return ""
}
func (c *AppleCardSharkImpl) PayForQuery(payFor payfor.PayforInfo) (string, string) {
cfg := config.Config{}
url, err := cfg.GetMFCardQueryUrl()
if err != nil {
return config.PAYFOR_FAIL, ""
}
params := map[string]string{}
params["order_id"] = payFor.BankOrderId
params["app_key"] = gojson.Json("").Get("appKey").Tostring()
req := httplib.Post(url)
marshal, err := json.Marshal(params)
if err != nil {
logs.Error("Map转化为byte数组失败,异常。", err)
// fmt.Printf("Map转化为byte数组失败,异常:%s\n", err)
return config.PAYFOR_FAIL, "内部错误请稍后再试试01"
}
logs.Info("请求参数:" + string(marshal))
req.Header("Content-Type", "application/json")
req.Body(marshal)
req.Header("Accept-Charset", "utf-8")
response, err := req.String()
if err != nil {
logs.Error("MF GetToken 请求失败:", err)
return config.PAYFOR_FAIL, ""
}
logs.Info("远端请求返回数据:" + response)
if gojson.Json(response).Get("code").Tostring() == "" {
logs.Error("远程调用失败")
return config.PAYFOR_BANKING, ""
}
if gojson.Json(response).Get("code").Tostring() == "0" {
type data struct {
OrderID int64 `json:"order_id"`
CardNo string `json:"card_no"`
CardPwd string `json:"card_pwd"`
Status int `json:"status"`
RspInfo string `json:"rsp_info"`
FaceVal int `json:"face_val"`
Amount int `json:"amount"`
Discount string `json:"discount"`
}
var d data
err2 := json.Unmarshal([]byte(gojson.Json(response).Get("data").Tostring()), &d)
if err2 != nil {
return config.PAYFOR_FAIL, ""
}
if d.Status == 9 {
return config.PAYFOR_SUCCESS, ""
}
if d.Status == 4 {
return config.PAYFOR_BANKING, ""
}
if d.Status == 7 || d.Status == 8 {
return config.PAYFOR_FAIL, ""
}
}
logs.Error("远程调用失败")
return config.PAYFOR_BANKING, ""
}
func (c *AppleCardSharkImpl) BalanceQuery(roadInfo road.RoadInfo) float64 {
return 0.00
}
func (c *AppleCardSharkImpl) PayForNotify() string {
return ""
}

View File

@@ -0,0 +1,16 @@
package third_party
import (
"gateway/internal/entities/supplier"
"testing"
)
func TestAppleCardSharkImpl_SendCard(t *testing.T) {
i := AppleCardSharkImpl{}
i.SendCard("", supplier.RedeemCardInfo{
FaceType: "100",
RecoveryType: "8",
CardNo: "123456789",
Data: "123",
}, "", "")
}

View File

@@ -14,6 +14,7 @@ var supplierCode2Name = map[string]string{
"TMALLGAME_EVALUATION": "天猫游戏充值(好评)",
"WalMart": "沃尔玛充值",
"WalMartSelf": "沃尔玛充值(自有)",
"APPLESHARK": "苹果itunes充值(苹果鲨鱼)",
}
var registerSupplier = make(map[string]supplier.PayInterface)
@@ -34,6 +35,8 @@ func init() {
logs.Notice(CheckSupplierByCode("WalMart"))
registerSupplier["WalMartSelf"] = new(WalmartSelfImpl)
logs.Notice(CheckSupplierByCode("WalMartSelf"))
registerSupplier["APPLESHARK"] = new(AppleCardSharkImpl)
logs.Notice(CheckSupplierByCode("APPLESHARK"))
}
func GetPaySupplierByCode(code string) supplier.PayInterface {

View File

@@ -0,0 +1,168 @@
package hidden
import (
"errors"
"fmt"
"github.com/beego/beego/v2/client/orm"
"reflect"
"strings"
"time"
)
type MerchantHiddenConfig struct {
Id int `orm:"column(id);pk"`
MerchantUid string `orm:"column(merchant_uid);size(255);null" description:"商户Id"`
MerchantDeployId int `orm:"column(merchant_deploy_id);null" description:"商户通道id"`
Amount int `orm:"column(amount);null" description:"金额"`
FaceAmount int `orm:"column(face_amount);null" description:"面额"`
DelayDuration int `orm:"column(delay_duration);null" description:"延迟时间"`
Enable int `orm:"column(enable);null" description:"是否启用"`
Strategy int `orm:"strategy;null" description:"策略"`
CreateAt time.Time `orm:"column(created_at);type(datetime);null" description:"创建时间"`
UpdateAt time.Time `orm:"column(updated_at);type(datetime);null" description:"更新时间"`
}
func (t *MerchantHiddenConfig) TableName() string {
return "merchant_hidden_config"
}
func init() {
orm.RegisterModel(new(MerchantHiddenConfig))
}
// AddMerchantHiddenConfig insert a new MerchantHiddenConfig into database and returns
// last inserted Id on success.
func AddMerchantHiddenConfig(m *MerchantHiddenConfig) (id int64, err error) {
o := orm.NewOrm()
id, err = o.Insert(m)
return
}
// GetMerchantHiddenConfigById retrieves MerchantHiddenConfig by Id. Returns error if
// Id doesn't exist
func GetMerchantHiddenConfigById(id int) (v *MerchantHiddenConfig, err error) {
o := orm.NewOrm()
v = &MerchantHiddenConfig{Id: id}
if err = o.Read(v); err == nil {
return v, nil
}
return nil, err
}
// GetAllMerchantHiddenConfig retrieves all MerchantHiddenConfig matches certain condition. Returns empty list if
// no records exist
func GetAllEnabledMerchantHiddenConfig(amount float64, merchantUid string, roadUid string) (v []*MerchantHiddenConfig, err error) {
o := orm.NewOrm()
v = []*MerchantHiddenConfig{}
_, err = o.QueryTable(new(MerchantHiddenConfig)).
Filter("face_amount", amount).Filter("merchant_uid", merchantUid).
Filter("merchant_deploy_id", roadUid).Filter("enable", 1).All(v)
return
}
func GetAllMerchantHiddenConfig(query map[string]string, fields []string, sortby []string, order []string,
offset int64, limit int64) (ml []interface{}, err error) {
o := orm.NewOrm()
qs := o.QueryTable(new(MerchantHiddenConfig))
// query k=v
for k, v := range query {
// rewrite dot-notation to Object__Attribute
k = strings.Replace(k, ".", "__", -1)
if strings.Contains(k, "isnull") {
qs = qs.Filter(k, (v == "true" || v == "1"))
} else {
qs = qs.Filter(k, v)
}
}
// order by:
var sortFields []string
if len(sortby) != 0 {
if len(sortby) == len(order) {
// 1) for each sort field, there is an associated order
for i, v := range sortby {
orderby := ""
if order[i] == "desc" {
orderby = "-" + v
} else if order[i] == "asc" {
orderby = v
} else {
return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
}
sortFields = append(sortFields, orderby)
}
qs = qs.OrderBy(sortFields...)
} else if len(sortby) != len(order) && len(order) == 1 {
// 2) there is exactly one order, all the sorted fields will be sorted by this order
for _, v := range sortby {
orderby := ""
if order[0] == "desc" {
orderby = "-" + v
} else if order[0] == "asc" {
orderby = v
} else {
return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
}
sortFields = append(sortFields, orderby)
}
} else if len(sortby) != len(order) && len(order) != 1 {
return nil, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1")
}
} else {
if len(order) != 0 {
return nil, errors.New("Error: unused 'order' fields")
}
}
var l []MerchantHiddenConfig
qs = qs.OrderBy(sortFields...)
if _, err = qs.Limit(limit, offset).All(&l, fields...); err == nil {
if len(fields) == 0 {
for _, v := range l {
ml = append(ml, v)
}
} else {
// trim unused fields
for _, v := range l {
m := make(map[string]interface{})
val := reflect.ValueOf(v)
for _, fname := range fields {
m[fname] = val.FieldByName(fname).Interface()
}
ml = append(ml, m)
}
}
return ml, nil
}
return nil, err
}
// UpdateMerchantHiddenConfig updates MerchantHiddenConfig by Id and returns error if
// the record to be updated doesn't exist
func UpdateMerchantHiddenConfigById(m *MerchantHiddenConfig) (err error) {
o := orm.NewOrm()
v := MerchantHiddenConfig{Id: m.Id}
// ascertain id exists in the database
if err = o.Read(&v); err == nil {
var num int64
if num, err = o.Update(m); err == nil {
fmt.Println("Number of records updated in database:", num)
}
}
return
}
// DeleteMerchantHiddenConfig deletes MerchantHiddenConfig by Id and returns error if
// the record to be deleted doesn't exist
func DeleteMerchantHiddenConfig(id int) (err error) {
o := orm.NewOrm()
v := MerchantHiddenConfig{Id: id}
// ascertain id exists in the database
if err = o.Read(&v); err == nil {
var num int64
if num, err = o.Delete(&MerchantHiddenConfig{Id: id}); err == nil {
fmt.Println("Number of records deleted in database:", num)
}
}
return
}

View File

@@ -0,0 +1,183 @@
package hidden
import (
"errors"
"fmt"
"reflect"
"strings"
"time"
"github.com/beego/beego/v2/client/orm"
)
type MerchantHiddenRecord struct {
Id int `orm:"column(id);pk"`
TargetOrderNo string `orm:"column(target_order_no);size(255);null" description:"原有id"`
ReplaceOrderNo int `orm:"column(replace_order_no);null" description:"替换掉的id"`
Amount int `orm:"column(amount);null" description:"偷卡金额"`
MerchantHiddenConfigId int `orm:"column(merchant_hidden_config_id);null" description:"关联偷卡规则"`
CreateAt time.Time `orm:"column(created_at);type(datetime);null" description:"创建时间"`
UpdateAt time.Time `orm:"column(updated_at);type(datetime);null" description:"修改时间"`
}
func (t *MerchantHiddenRecord) TableName() string {
return "merchant_hidden_record"
}
func init() {
orm.RegisterModel(new(MerchantHiddenRecord))
}
// AddMerchantHiddenRecord insert a new MerchantHiddenRecord into database and returns
// last inserted Id on success.
func AddMerchantHiddenRecord(m *MerchantHiddenRecord) (id int64, err error) {
o := orm.NewOrm()
id, err = o.Insert(m)
return
}
// GetMerchantHiddenRecordById retrieves MerchantHiddenRecord by Id. Returns error if
// Id doesn't exist
func GetMerchantHiddenRecordById(id int) (v *MerchantHiddenRecord, err error) {
o := orm.NewOrm()
v = &MerchantHiddenRecord{Id: id}
if err = o.Read(v); err == nil {
return v, nil
}
return nil, err
}
// GetMerchantHiddenRecordByHiddenConfigId retrieves MerchantHiddenRecord by Id. Returns error if
// Id doesn't exist
func GetMerchantHiddenRecordByHiddenConfigId(id int) (v *MerchantHiddenRecord, err error) {
o := orm.NewOrm()
v = &MerchantHiddenRecord{MerchantHiddenConfigId: id}
if err = o.Read(v); err == nil {
return v, nil
}
return nil, err
}
// GetManyMerchantHiddenRecordByHiddenConfigId retrieves MerchantHiddenRecord by Id. Returns error if
// Id doesn't exist
func GetManyMerchantHiddenRecordByHiddenConfigId(id int) (v []*MerchantHiddenRecord, err error) {
v = []*MerchantHiddenRecord{}
o := orm.NewOrm()
_, err = o.QueryTable(new(MerchantHiddenRecord)).Filter("merchant_hidden_config_id", id).OrderBy("created_at").All(&v)
return nil, err
}
// GetOneMerchantHiddenRecordByHiddenConfigId 查询最新的偷卡记录
func GetOneMerchantHiddenRecordByHiddenConfigId(id int) (v *MerchantHiddenRecord, err error) {
o := orm.NewOrm()
_, err = o.QueryTable(new(MerchantHiddenRecord)).Filter("merchant_hidden_config_id", id).Limit(1).OrderBy("created_at").All(&v)
return nil, err
}
// GetAllMerchantHiddenRecord retrieves all MerchantHiddenRecord matches certain condition. Returns empty list if
// no records exist
func GetAllMerchantHiddenRecord(query map[string]string, fields []string, sortby []string, order []string,
offset int64, limit int64) (ml []interface{}, err error) {
o := orm.NewOrm()
qs := o.QueryTable(new(MerchantHiddenRecord))
// query k=v
for k, v := range query {
// rewrite dot-notation to Object__Attribute
k = strings.Replace(k, ".", "__", -1)
if strings.Contains(k, "isnull") {
qs = qs.Filter(k, (v == "true" || v == "1"))
} else {
qs = qs.Filter(k, v)
}
}
// order by:
var sortFields []string
if len(sortby) != 0 {
if len(sortby) == len(order) {
// 1) for each sort field, there is an associated order
for i, v := range sortby {
orderby := ""
if order[i] == "desc" {
orderby = "-" + v
} else if order[i] == "asc" {
orderby = v
} else {
return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
}
sortFields = append(sortFields, orderby)
}
qs = qs.OrderBy(sortFields...)
} else if len(sortby) != len(order) && len(order) == 1 {
// 2) there is exactly one order, all the sorted fields will be sorted by this order
for _, v := range sortby {
orderby := ""
if order[0] == "desc" {
orderby = "-" + v
} else if order[0] == "asc" {
orderby = v
} else {
return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
}
sortFields = append(sortFields, orderby)
}
} else if len(sortby) != len(order) && len(order) != 1 {
return nil, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1")
}
} else {
if len(order) != 0 {
return nil, errors.New("Error: unused 'order' fields")
}
}
var l []MerchantHiddenRecord
qs = qs.OrderBy(sortFields...)
if _, err = qs.Limit(limit, offset).All(&l, fields...); err == nil {
if len(fields) == 0 {
for _, v := range l {
ml = append(ml, v)
}
} else {
// trim unused fields
for _, v := range l {
m := make(map[string]interface{})
val := reflect.ValueOf(v)
for _, fname := range fields {
m[fname] = val.FieldByName(fname).Interface()
}
ml = append(ml, m)
}
}
return ml, nil
}
return nil, err
}
// UpdateMerchantHiddenRecord updates MerchantHiddenRecord by Id and returns error if
// the record to be updated doesn't exist
func UpdateMerchantHiddenRecordById(m *MerchantHiddenRecord) (err error) {
o := orm.NewOrm()
v := MerchantHiddenRecord{Id: m.Id}
// ascertain id exists in the database
if err = o.Read(&v); err == nil {
var num int64
if num, err = o.Update(m); err == nil {
fmt.Println("Number of records updated in database:", num)
}
}
return
}
// DeleteMerchantHiddenRecord deletes MerchantHiddenRecord by Id and returns error if
// the record to be deleted doesn't exist
func DeleteMerchantHiddenRecord(id int) (err error) {
o := orm.NewOrm()
v := MerchantHiddenRecord{Id: id}
// ascertain id exists in the database
if err = o.Read(&v); err == nil {
var num int64
if num, err = o.Delete(&MerchantHiddenRecord{Id: id}); err == nil {
fmt.Println("Number of records deleted in database:", num)
}
}
return
}

View File

@@ -150,3 +150,13 @@ func GetMerchantDeployByHour(hour int) []MerchantDeployInfo {
return merchantDeployList
}
func GetById(id int) *MerchantDeployInfo {
o := orm.NewOrm()
var merchantDeploy *MerchantDeployInfo
err := o.QueryTable(MERCHANT_DEPLOY_INFO).Filter("id", id).One(&merchantDeploy)
if err != nil {
logs.Error("get merchant deploy by id fail: ", err)
}
return merchantDeploy
}

View File

@@ -2,6 +2,7 @@ package order
import (
"fmt"
"github.com/rs/xid"
"strconv"
"time"
@@ -64,6 +65,15 @@ func InsertOrder(orderInfo OrderInfo) bool {
return true
}
func HiddenOrder(info *OrderInfo) (bankOrderId string, err error) {
bankOrderId = "6666" + xid.New().String()
_, err = orm.NewOrm().QueryTable(ORDER_INFO).Filter("id", info.Id).Update(orm.Params{
"bank_order_id": bankOrderId,
"merchant_order_id": "", // 现有订单id为空
})
return
}
// UpdateIpRestricted 修改IP限制状态
func UpdateIpRestricted(orderId string, isIpRestricted bool) bool {
o := orm.NewOrm()
@@ -268,3 +278,20 @@ func InsertPayTime(merchantOrderId string) bool {
})
return err == nil
}
func GetByUidAndRoadUid(uid string, roadUid string) (info []*OrderInfo, err error) {
o := orm.NewOrm()
_, err = o.QueryTable(ORDER_INFO).Filter("merchant_uid", uid).Filter("road_uid", roadUid).Filter("status", "success").All(&info)
return
}
// GetByUidAndRoadUidAndTime 根据时间和筛选条件查询
func GetByUidAndRoadUidAndTime(uid string, roadUid string, createTime time.Time) (info []*OrderInfo, err error) {
o := orm.NewOrm()
_, err = o.QueryTable(ORDER_INFO).Filter("merchant_uid", uid).
Filter("create_time__gte", createTime).
Filter("road_uid", roadUid).
Filter("status", "success").
All(&info)
return
}

View File

@@ -26,6 +26,7 @@ func init() {
web.Router("/mfcard/notifyV2", &third_party.MFCardV2Impl{}, "*:PayNotify")
web.Router("/appleCard/notify", &third_party.AppleCardImpl{}, "*:PayNotify")
web.Router("/appleSharkCard/notify", &third_party.AppleCardSharkImpl{}, "*:PayNotify")
web.Router("/tMallGame/notify", &third_party.TMAllGameImpl{}, "*:PayNotify")
web.Router("/jdCard/notify", &third_party.JDCardImpl{}, "*:PayNotify")
web.Router("/walMart/notify", &third_party.WalMartImpl{}, "*:PayNotify")

View File

@@ -0,0 +1,107 @@
package service
import (
"encoding/json"
"gateway/internal/entities/supplier"
"gateway/internal/models/hidden"
"gateway/internal/models/merchant_deploy"
"gateway/internal/models/order"
"github.com/duke-git/lancet/v2/pointer"
"github.com/duke-git/lancet/v2/random"
"github.com/duke-git/lancet/v2/slice"
"github.com/duke-git/lancet/v2/validator"
"github.com/mohae/deepcopy"
"time"
)
// GetOrderHidden 判断当前订单是否偷卡
func GetOrderHidden(orderNo *order.OrderInfo) (cfg *hidden.MerchantHiddenConfig) {
// 查询当前订单是否处于偷卡的范围
configList, err := hidden.GetAllEnabledMerchantHiddenConfig(orderNo.OrderAmount, orderNo.MerchantUid, orderNo.RoadUid)
if err != nil {
return
}
for _, config := range configList {
merchantDeploy := merchant_deploy.GetById(config.MerchantDeployId)
if merchantDeploy == nil {
continue
}
record, err2 := hidden.GetOneMerchantHiddenRecordByHiddenConfigId(config.Id)
if err2 != nil {
continue
}
//如果是第一次偷卡
if record == nil {
//查询当前用户所有的订单
orderInfos, err := order.GetByUidAndRoadUid(merchantDeploy.MerchantUid, merchantDeploy.SingleRoadUid)
if err != nil || len(orderInfos) == 0 {
continue
}
//计算订单金额
amountTotal := slice.ReduceBy(orderInfos, 0, func(index int, item *order.OrderInfo, agg int) int {
return agg + int(item.FactAmount)
})
//如果当前金额超过设定金额,并且面额一致,就偷卡
if int(amountTotal) > config.Amount && orderNo.FactAmount == float64(config.FaceAmount) {
cfg = config
return
}
continue
}
//如果是多次偷卡
orderInfos, err := order.GetByUidAndRoadUidAndTime(merchantDeploy.MerchantUid, merchantDeploy.SingleRoadUid, record.CreateAt)
if err != nil || len(orderInfos) == 0 {
continue
}
//计算订单金额
amountTotal := slice.ReduceBy(orderInfos, 0, func(index int, item *order.OrderInfo, agg int) int {
return agg + int(item.FactAmount)
})
//如果当前金额超过设定金额,并且面额一致,就偷卡
if int(amountTotal) > config.Amount && orderNo.FactAmount == float64(config.FaceAmount) {
cfg = config
return
}
}
return
}
func HiddenBlankOrder(orderInfo *order.OrderInfo, duration int64) (bankOrderId string, err error) {
//复制到一个新的对象中
newOrderInfo := deepcopy.Copy(*orderInfo).(*order.OrderInfo)
// 创建一个新的失败订单
newOrderInfo.CreateTime = orderInfo.CreateTime.Add(time.Second * time.Duration(duration))
newOrderInfo.UpdateTime = orderInfo.UpdateTime.Add(time.Second * time.Duration(duration))
newOrderInfo.ExValue = "{}"
order.InsertOrder(*newOrderInfo)
// 去掉现有订单的关联数据
bankOrderId, err = order.HiddenOrder(orderInfo)
return
}
func HiddenErrorOrder(orderInfo *order.OrderInfo, duration int64) (bankOrderId string, err error) {
//复制到一个新的对象中
newOrderInfo := deepcopy.Copy(*orderInfo).(*order.OrderInfo)
// 创建一个新的失败订单
newOrderInfo.CreateTime = orderInfo.CreateTime.Add(time.Second * time.Duration(duration))
newOrderInfo.UpdateTime = orderInfo.UpdateTime.Add(time.Second * time.Duration(duration))
exValue := supplier.RedeemCardInfo{}
if err = json.Unmarshal([]byte(newOrderInfo.ExValue), &exValue); err != nil {
return
}
//字符串转数组
targetStr := random.RandFromGivenSlice([]string{exValue.Data})
if !pointer.IsNil(targetStr) && targetStr != "" {
index := slice.IndexOf([]string{exValue.Data}, targetStr)
if index != -1 {
if validator.IsNumber(targetStr) {
// 替换一个其他数字
//exValue.Data = random.RandFromGivenSlice([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"})
}
}
}
order.InsertOrder(*newOrderInfo)
// 去掉现有订单的关联数据
bankOrderId, _ = order.HiddenOrder(orderInfo)
return
}

View File

@@ -305,6 +305,7 @@ func GenerateRecord(c *response.PayBaseResp) (order.OrderInfo, order.OrderProfit
if c.Code == -1 {
return orderInfo, orderProfit, errors.New("订单数据插入失败")
}
//插入订单记录和订单利润记录
if !order.InsertOrderAndOrderProfit(orderInfo, orderProfit) {
c.Code = -1
return orderInfo, orderProfit, errors.New("订单数据插入失败")