Compare commits

..

No commits in common. "main" and "custom-error" have entirely different histories.

154 changed files with 1139 additions and 7921 deletions

4
.gitignore vendored
View File

@ -8,6 +8,4 @@ Thumbs.db
# project
pkg/
cert/
cmd/
plugins/Makefile
cert/

103
Makefile
View File

@ -1,102 +1,81 @@
build-linux:
@echo "===== 开始编译 Linux 插件 ====="
@[ -d "plugins/${name}" ] || ( \
echo "错误:插件目录 plugins/${name} 不存在"; \
exit 1 \
)
cd plugins; \
export GOOS=linux; \
export GOARCH=amd64; \
export CGO_ENABLED=0; \
export GOPROXY=https://goproxy.cn,direct; \
cd ${name} && go build -o ../../pkg/linux/${name}.so .
@echo "===== 编译完成 ====="
build-mac:
@echo "===== 开始编译 mac 插件 ====="
cd plugins; \
export GOOS=darwin; \
export GO111MODULE=on; \
export CGO_ENABLED=0; \
export GOPROXY=https://goproxy.cn,direct; \
cd ${name} && go build -o ../../pkg/mac/${name}.so .
@echo "===== 编译完成 ====="
build-win:
@echo "===== 开始编译 win 插件 ====="
cd plugins; \
export GOOS=windows; \
export GOARCH=amd64; \
export GO111MODULE=on; \
export CGO_ENABLED=0; \
export GOPROXY=https://goproxy.cn,direct; \
cd ${name} && go build -o ../../pkg/win/${name}.so .
@echo "===== 编译完成 ====="
cd ${name} && go build -o ../../pkg/win/${name}.so .
build-linux:
cd plugins; \
export GOOS=linux; \
export GOARCH=amd64; \
export CGO_ENABLED=0; \
export GOPROXY=https://goproxy.cn,direct; \
cd ${name} && go build -o ../../pkg/linux/${name}.so .
.PHONY: zltx_v1
zltx_v1:
make build-mac name=zltx_v1 && \
make build-linux name=zltx_v1 && \
make build-mac name=zltx_v1
make build-win name=zltx_v1
.PHONY: zltx_card_v1
zltx_card_v1:
make build-mac name=zltx_card_v1 && \
make build-linux name=zltx_card_v1 && \
make build-mac name=zltx_card_v1
make build-win name=zltx_card_v1
.PHONY: zltx_v2
zltx_v2:
make build-mac name=zltx_v2 && \
make build-linux name=zltx_v2 && \
make build-mac name=zltx_v2
.PHONY: alipay_cpn
alipay_cpn:
make build-linux name=alipay_cpn && \
make build-mac name=alipay_cpn
.PHONY: alipay_redpack
alipay_redpack:
make build-linux name=alipay_redpack && \
make build-mac name=alipay_redpack
.PHONY: wechat_cpn
wechat_cpn:
make build-linux name=wechat_cpn && \
make build-mac name=wechat_cpn
.PHONY: wechat_redpack
wechat_redpack:
make build-linux name=wechat_redpack && \
make build-mac name=wechat_redpack
.PHONY: wechat_redpack_v2
wechat_redpack_v2:
make build-linux name=wechat_redpack_v2 && \
make build-mac name=wechat_redpack_v2
make build-win name=zltx_v2
.PHONY: union_pay_cpn
union_pay_cpn:
make build-mac name=union_pay_cpn && \
make build-linux name=union_pay_cpn && \
make build-mac name=union_pay_cpn
make build-win name=union_pay_cpn
.PHONY: union_pay_redpack
union_pay_redpack:
make build-mac name=union_pay_redpack && \
make build-linux name=union_pay_redpack && \
make build-mac name=union_pay_redpack
make build-win name=union_pay_redpack
.PHONY: qixing_alipay_redpack
qixing_alipay_redpack:
make build-linux name=qixing_alipay_redpack && \
make build-mac name=qixing_alipay_redpack
.PHONY: alipay_cpn
alipay_cpn:
make build-mac name=alipay_cpn && \
make build-linux name=alipay_cpn && \
make build-win name=alipay_cpn
.PHONY: qixing_wechat_redpack
qixing_wechat_redpack:
make build-linux name=qixing_wechat_redpack && \
make build-mac name=qixing_wechat_redpack
.PHONY: alipay_redpack
alipay_redpack:
make build-mac name=alipay_redpack && \
make build-linux name=alipay_redpack && \
make build-win name=alipay_redpack
.PHONY: lsxd_wechat_cpn
lsxd_wechat_cpn:
make build-linux name=lsxd_wechat_cpn && \
make build-mac name=lsxd_wechat_cpn
.PHONY: wechat_cpn
wechat_cpn:
make build-mac name=wechat_cpn && \
make build-linux name=wechat_cpn && \
make build-win name=wechat_cpn
.PHONY: wechat_redpack
wechat_redpack:
make build-mac name=wechat_redpack && \
make build-linux name=wechat_redpack && \
make build-win name=wechat_redpack
.PHONY: all
all: zltx_v1 zltx_card_v1 zltx_v2 alipay_cpn alipay_redpack wechat_cpn wechat_redpack wechat_redpack_v2 qixing_wechat_redpack
all: zltx_v1 zltx_card_v1 union_pay_cpn union_pay_redpack alipay_cpn alipay_redpack wechat_cpn wechat_redpack zltx_v2

109
cmd/alipay_redpack.go Normal file
View File

@ -0,0 +1,109 @@
package main
import (
"context"
"encoding/json"
"gitea.cdlsxd.cn/sdk/plugin/instance"
"gitea.cdlsxd.cn/sdk/plugin/manage"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"log"
)
var alpayRedConf = &manage.Config{
Cmd: "pkg/mac/alipay_redpack.so",
Tag: "alipay_redpack",
Version: 1,
CookieKey: "alipay_redpack",
CookieValue: "alipay_redpack",
}
func getAlpayRedConf() []byte {
type Config struct {
AppId string `json:"app_id"`
Prk string `json:"prk"` // 私钥
}
c := &Config{
AppId: "2021004100663111",
Prk: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDbA+YuMp4JUVj6rjzgwGKNXWkEMGX/rinqkfyBZ6B6p8EKz8zgA+ypiJLOixD3GyKnUnAzx4waNRZHfdEu+l57kJFtd/ipfwtJ28aTi7TqtqEpqD+UPY4ourt2CuyCFxWsonS6dczqtTvfAVArTdbGJYY+kNNVR3WiXgGUhkUu8N7vEowU00RUQGNdSVMUs4FX+HlU3RnEoRc/xUhPiaLf0Bm/g9wG96kwyg/TZvkNU7PpMVRdXeLrVORn0qThs3VA4dqondF+O12iC1TK4TKYGzFYczGAUsfuurtDyCc2GMoE+hH2FR8U7amQOVuYZFkutTdqaqukWpFQOr8wLeMzAgMBAAECggEAD715/3v3y6ejA3EeQvDQpGRANeLckcGMlaUkRpCSAf6oawSALuFZUt3T3zAzae7zUJ8mHTKMKR4DmeO68utfevXq3bkvj87nmslGvjfeKrgxYPMMjrTV0KuK6XLjiH3sOtn6FaR9s6iOwvovLs2LT/ZGbZyu84QNOjwTVP9JXZQkBgMItdKf+U3H2Cjp7U/qXBt8/9yzVFklp1g1883DAty0lzmT27dJimGVGaPQ8vNxo81+ZUEJAn6GUTk0K/GwJfhPTU8hh8G90n2LTyskoMjGxQe9lXfCcS9DmWawEQL4WTctPrDYlnS/cjCVMS0KXIFuxRNf6qaMYDeywC8BgQKBgQDyf40dvmw34Rrb46+NLayQ9W5CJI/dYeRajpCjoOomq5QYhbXzUCpVfbtByeGMsg3zN58NNsZGhl5SU0GdEdoOlxCk+2Hey2yQYF4ugQm/dTd68Jgqi3yujigAbNYa1ZhL9t3FqouPY1dGiaxl+DFYdSMIrsVFXh2NbrPyqTk5IQKBgQDnNaH7LCcIUqg9H8Tsls+8GLzP2HwF3hdll8asEsF3K3HX6/Zlp4VnnEcIkAxLRL/L0o5akXrmA18ZwfoSguTPXV9va3G2GgIiJHgcytmGtQVvbpFKuPnCXKz+avxnfO0flJqyYEuHr/40jsGbMkk0Kr52/n3ivXZbBUlT+tkt0wKBgB0qLgSnxEgsMJjFl3V5SsncWrhlwU+02Evz3X1wevjPpe4VFr7+ozjI+F5/MztCpt7bj6t9LPeKbYmlLb0ASqN6k6vj9+9ds97hWDJrnoqCRHvqt8JWKFauDi2O6WksyzZHqIB/dG14WyTGpg9VfEnRPLdsnZksKo26BLZol9NBAoGBAMz7oMNljqlzVtLyMo2q2zuhFuySusoc79NTL4FpE3rK2qCbA5V2YvDL/bIau7uTlRNodmrXZgU84fidIE9/Gsq5tp26vVK8Vj3c5Vxpf1dNcCct+MQtoMjvjzP0uBgsCrKf9lLEytHed1ozYnRsrbgBWWF4GTWH0cG6uxsoX5mfAoGAfYXKZdyRU+Su4y4EnDLMXd320ar7PaeuY8aZU7V6UQEsaOj6H3O8JMEOuBrOVEhAP2EkAC8ayargSXTSOkN97pg88agKDwA6jh4N6TKAK8XMft81YPPliVwZMsAUqihSKQBnKZ7ssHHLGWWWp5vfkyb7Y7dIkZcPzB0X3q/jL58=",
}
marshal, _ := json.Marshal(c)
return marshal
}
func alipayOrderRedPack() {
err := manage.Add(alpayRedConf)
if err != nil {
log.Fatalln(err)
}
defer manage.Close()
type ProductExtra struct {
Wishing string `json:"wishing"`
}
productExtra := ProductExtra{Wishing: "wishing"}
productExtraBytes, err := json.Marshal(productExtra)
if err != nil {
log.Fatalln(err)
}
request := &proto.OrderRequest{
Config: getAlpayRedConf(),
Order: &proto.OrderRequest_Order{
OrderNo: "lsxd202406071545141534",
Account: "123",
Quantity: 1,
Amount: 0.01,
Extra: []byte(`{"name":"李1子铭"}`),
},
Product: &proto.OrderRequest_Product{
ProductNo: "",
Price: 0.01,
Extra: productExtraBytes,
},
}
log.Printf("Order request.Product.Extra:%s", string(request.Product.Extra))
result, err := instance.Order(context.Background(), alpayRedConf.Tag, request)
if err != nil {
log.Fatalln(err)
}
log.Printf("Order res:%+v", result)
}
func alipayQueryRedPack() {
err := manage.Add(alpayRedConf)
if err != nil {
log.Fatalln(err)
}
defer manage.Close()
queryRequest := &proto.QueryRequest{
Config: getAlpayRedConf(),
Order: &proto.QueryRequest_Order{
OrderNo: "202411191746471970010022",
TradeNo: "20241119020070011560880008002610",
Account: "",
Extra: []byte(``),
},
}
resQuery, err := instance.Query(context.Background(), alpayRedConf.Tag, queryRequest)
if err != nil {
log.Fatalln(err)
}
log.Printf("Query res:%+v", resQuery)
}
func alipayNotifyRedPack() {
err := manage.Add(alpayRedConf)
if err != nil {
log.Fatalln(err)
}
defer manage.Close()
req := &proto.NotifyRequest{
Config: getAlpayRedConf(),
Queries: []byte(`{"charset":"UTF-8","biz_content":"{\"pay_date\":\"2023-05-24 14:54:46\",\"biz_scene\":\"DIRECT_TRANSFER\",\"action_type\":\"FINISH\",\"pay_fund_order_id\":\"20230524110070001506320098515414\",\"origin_interface\":\"alipay.fund.trans.uni.transfer\",\"out_biz_no\":\"lsxd202305241454456670\",\"trans_amount\":\"0.01\",\"product_code\":\"STD_RED_PACKET\",\"order_id\":\"20230524110070000006320080320326\",\"status\":\"SUCCESS\"}","utc_timestamp":"1684911287217","sign":"JMNyQDFvtp37/99ZzzjJOrKe7fQ3nnUR124q/mh6MIztMn2w95PjI5MReqdEwy4lYvCKmXERDPt+vEpYuzlqM+98hWY1JwMdJSNC0PHvqmFqKIQchOzQavUv7VxCYGhdQ3lgH51KEQV2w5j27HkoH71jmQC1duMuzyrJVq9uYkKrTyKrnzceg8t7mYNOy1FxUsCnvV1s6MFc4OWXOhAFAUQPjFX7UiCImTKLV1XW93DjnS+skui7mt9xD0Nn0L3Tk23grHA3FLOjEJVTG2ic32ifKUAy54XS48J3i1Abssu4K1iB0I7o/s110MpgZl6HY/T6geG6KpBikrO1S/WvjQ==","app_id":"2021003196667150","version":"1.1","sign_type":"RSA2","notify_id":"2023052401222145447048481441455595","msg_method":"alipay.fund.trans.order.changed"}`),
Headers: []byte(``),
Body: []byte(``),
}
resNotify, err := instance.Notify(context.Background(), alpayRedConf.Tag, req)
if err != nil {
log.Fatalln(err)
}
log.Printf("Notify res:%+v", resNotify)
}

6
cmd/main.go Normal file
View File

@ -0,0 +1,6 @@
package main
// main 这只是一个演示
func main() {
alipayOrderRedPack()
}

75
cmd/wechat_cpn.go Normal file
View File

@ -0,0 +1,75 @@
package main
import (
"context"
"encoding/json"
"gitea.cdlsxd.cn/sdk/plugin/instance"
"gitea.cdlsxd.cn/sdk/plugin/manage"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"log"
"plugins/utils/wechat"
)
var wechatCpnConf = &manage.Config{
Cmd: "pkg/mac/wechat_cpn.so",
Tag: "alipay_redpack",
Version: 1,
CookieKey: "wechat_cpn",
CookieValue: "wechat_cpn",
}
func getWechatCpnConf() []byte {
c := &wechat.Server{
MchID: "1605446142", // 证书所属商户
MchCertificateSerialNumber: "4D081089DEB385316CBDCB55C070287E4920AC76",
}
marshal, _ := json.Marshal(c)
return marshal
}
func wechatOrderCpn() {
err := manage.Add(wechatCpnConf)
if err != nil {
log.Fatalln(err)
}
defer manage.Close()
request := &proto.OrderRequest{
Config: getWechatCpnConf(),
Order: &proto.OrderRequest_Order{
OrderNo: "202411221528258970010009",
Account: "oknbq5tQyff_vCjjRjCJBElhF1og",
Extra: []byte(`{"app_id":"wx83fd6da8093f55b7","stock_creator_mchid":"1679625521"}`),
},
Product: &proto.OrderRequest_Product{
ProductNo: "19519911",
Extra: []byte(`{}`),
},
}
res, err := instance.Order(context.Background(), alpayRedConf.Tag, request)
if err != nil {
log.Fatalln(err)
}
log.Printf("Order res:%+v", res)
}
func wechatQueryCpn() {
err := manage.Add(wechatCpnConf)
if err != nil {
log.Fatalln(err)
}
defer manage.Close()
queryRequest := &proto.QueryRequest{
Config: getWechatCpnConf(),
Order: &proto.QueryRequest_Order{
OrderNo: "",
TradeNo: "69445765514",
Account: "oO3vO5AxRWgTjmMD38FTvnB5Rq6o",
Extra: []byte(`{"app_id":"wx9ed74283ad25bca1"}`),
},
}
resQuery, err := instance.Query(context.Background(), alpayRedConf.Tag, queryRequest)
if err != nil {
log.Fatalln(err)
}
log.Printf("Query res:%+v", resQuery)
}

82
cmd/wechat_redpack.go Normal file
View File

@ -0,0 +1,82 @@
package main
import (
"context"
"encoding/json"
"gitea.cdlsxd.cn/sdk/plugin/instance"
"gitea.cdlsxd.cn/sdk/plugin/manage"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"log"
"plugins/utils/wechat"
)
var wechatRedPackConf = &manage.Config{
Cmd: "pkg/mac/wechat_redpack.so",
Tag: "wechat_redpack",
Version: 1,
CookieKey: "wechat_redpack",
CookieValue: "wechat_redpack",
}
func getWechatRedPackConf() []byte {
c := &wechat.Server{
MchID: "1629276485",
MchCertificateSerialNumber: "3C7E21B74C04BE6227A690EB44184F219D763F92",
}
marshal, _ := json.Marshal(c)
return marshal
}
func wechatRedPackOrder() {
err := manage.Add(wechatRedPackConf)
if err != nil {
log.Fatalln(err)
}
defer manage.Close()
//芭乐福利官wxbfcf9fd93f390c3a
//芭乐芭乐加油wx381e6a04ab7f69b6
req := &proto.OrderRequest{
Config: getWechatRedPackConf(),
Order: &proto.OrderRequest_Order{
OrderNo: "240403164049635937",
Account: "oZ5ge5Vb2UvbEcIc8ZTt_yF7fNx8",
Amount: 0.01,
Quantity: 1,
Extra: []byte(`{"app_id":"123456", "out_detail_no":"12345678917"}`),
},
Product: &proto.OrderRequest_Product{
ProductNo: "",
Extra: []byte(`{"batch_name":"batch_name", "batch_remark":"batch_remark"}`),
},
}
res, err := instance.Order(context.Background(), wechatRedPackConf.Tag, req)
if err != nil {
log.Fatalln(err)
}
log.Printf("Order res:%+v", res)
}
func wechatRedPackQuery() {
err := manage.Add(wechatRedPackConf)
if err != nil {
log.Fatalln(err)
}
defer manage.Close()
//芭乐福利官wxbfcf9fd93f390c3a
//芭乐芭乐加油wx381e6a04ab7f69b6
//{status:ING order_no:"240403164049635937" trade_no:"131000609002201499912952024102122461843179" message:"成功"}
queryRequest := &proto.QueryRequest{
Config: getWechatRedPackConf(),
Order: &proto.QueryRequest_Order{
OrderNo: "202410231046525199060462",
TradeNo: "",
Account: "",
Extra: []byte(`{"out_detail_no":"20241023104652519906046250731390"}`),
},
}
resQuery, err := instance.Query(context.Background(), wechatRedPackConf.Tag, queryRequest)
if err != nil {
log.Fatalln(err)
}
log.Printf("Query res:%+v", resQuery)
}

74
cmd/zltx.go Normal file
View File

@ -0,0 +1,74 @@
package main
import (
"context"
"encoding/json"
"gitea.cdlsxd.cn/sdk/plugin/instance"
"gitea.cdlsxd.cn/sdk/plugin/manage"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"log"
)
var zltxConf = &manage.Config{
Cmd: "pkg/mac/zltx.so",
Tag: "zltx",
Version: 1,
CookieKey: "zltx",
CookieValue: "zltx",
}
func config() []byte {
type Config struct {
AppId string `json:"app_id"`
AppKey string `json:"app_key"`
BaseUri string `json:"base_uri"`
NotifyUrl string `json:"notify_url"`
}
c := &Config{
AppId: "23329",
AppKey: "8db16e8cc8363ed4eb4c14f9520bcc32",
BaseUri: "http://test.openapi.1688sup.cn",
NotifyUrl: "http://test.openapi.1688sup.cn",
}
marshal, _ := json.Marshal(c)
return marshal
}
// main 这只是一个演示
func zltx() {
err := manage.Add(zltxConf)
if err != nil {
log.Fatalln(err)
}
defer manage.Close()
orderRequest := &proto.OrderRequest{
Config: config(),
Order: &proto.OrderRequest_Order{
OrderNo: "test_main_zltx_6",
Account: "18666666666",
Quantity: 1,
Extra: nil,
},
Product: &proto.OrderRequest_Product{
ProductNo: "106",
Extra: []byte(`{}`),
},
}
queryRequest := &proto.QueryRequest{
Config: config(),
Order: &proto.QueryRequest_Order{
OrderNo: "test_main_zltx_6",
},
}
res, err := instance.Order(context.Background(), zltxConf.Tag, orderRequest)
if err != nil {
log.Fatalln(err)
}
log.Printf("Order res:%+v", res)
resQuery, err := instance.Query(context.Background(), zltxConf.Tag, queryRequest)
if err != nil {
log.Fatalln(err)
}
log.Printf("Query res:%+v", resQuery)
}

View File

@ -1,35 +1,35 @@
module plugins/wechat_redpack_v2
module plugins
go 1.22.2
replace plugins/utils => ../../utils
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.27
github.com/go-playground/validator/v10 v10.22.0
github.com/hashicorp/go-plugin v1.6.1
github.com/wechatpay-apiv3/wechatpay-go v0.2.18
plugins/utils v1.0.0
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca
plugins/utils v0.0.0-00010101000000-000000000000
)
replace plugins/utils => ./utils
require (
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/hashicorp/go-hclog v0.14.1 // indirect
github.com/hashicorp/go-hclog v1.0.0 // indirect
github.com/hashicorp/go-plugin v1.6.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.10 // indirect
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
github.com/oklog/run v1.0.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/wechatpay-apiv3/wechatpay-go v0.2.18 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/sys v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/grpc v1.64.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect

View File

@ -1,5 +1,5 @@
gitea.cdlsxd.cn/sdk/plugin v1.0.27 h1:ocp+3n8qLlPm+yI0VpMXFWfC8gsdDrKxEowuD58T6CE=
gitea.cdlsxd.cn/sdk/plugin v1.0.27/go.mod h1:4fLMp/xB9GEBa3nJi62kXpHh7wnb9Lwjf0I8Vjaasx0=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca h1:snL161P7OynMA8hRVMLDjwnzZA2Q4mePg/iT/dyIfzA=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
@ -11,8 +11,6 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-kratos/kratos/v2 v2.8.2 h1:EsEA7AmPQ2YQQ0FZrDWO2HgBNqeWM8z/mWKzS5UkQaQ=
github.com/go-kratos/kratos/v2 v2.8.2/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -25,8 +23,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.0.0 h1:bkKf0BeBXcSYa7f5Fyi9gMuQ8gNsxeiNpZjR6VxNZeo=
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI=
github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
@ -44,6 +42,8 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -53,20 +53,20 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/wechatpay-apiv3/wechatpay-go v0.2.18 h1:vj5tvSmnEIz3ZsnFNNUzg+3Z46xgNMJbrO4aD4wP15w=
github.com/wechatpay-apiv3/wechatpay-go v0.2.18/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=

27
plugins/Makefile Normal file
View File

@ -0,0 +1,27 @@
.PHONY: tidy
tidy:
find . -type d -depth 1 -print | xargs -L 1 bash -c 'cd $$0 && pwd && go get -u gitea.cdlsxd.cn/sdk/plugin && go mod tidy'
.PHONY: build
build:
cd ${name} && go build -o ../../pkg/${name}.so .
.PHONY: zltx
zltx:
make build name=zltx
.PHONY: union_pay_cpn
union_pay_cpn:
make build name=union_pay_cpn
.PHONY: alipay_cpn
alipay_cpn:
make build name=alipay_cpn
.PHONY: wechat_cpn
wechat_cpn:
make build name=wechat_cpn
.PHONY: all
all:
find . -type d -depth 1 -print | xargs -L 1 bash -c 'cd $$0 && pwd && go build -o ../../pkg/$$0.exe'

View File

@ -5,10 +5,11 @@ go 1.22.2
replace plugins/utils => ../../utils
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.27
gitea.cdlsxd.cn/sdk/plugin v1.0.6
github.com/carlmjohnson/requests v0.24.2
github.com/go-playground/validator/v10 v10.22.0
github.com/hashicorp/go-plugin v1.6.1
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.9.0
plugins/utils v0.0.0-00010101000000-000000000000
)
@ -16,7 +17,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect

View File

@ -1,15 +1,17 @@
gitea.cdlsxd.cn/sdk/plugin v1.0.27 h1:ocp+3n8qLlPm+yI0VpMXFWfC8gsdDrKxEowuD58T6CE=
gitea.cdlsxd.cn/sdk/plugin v1.0.27/go.mod h1:4fLMp/xB9GEBa3nJi62kXpHh7wnb9Lwjf0I8Vjaasx0=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca h1:snL161P7OynMA8hRVMLDjwnzZA2Q4mePg/iT/dyIfzA=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
gitea.cdlsxd.cn/sdk/plugin v1.0.6 h1:fcQrgdRT4zVmxqK8rsB4Oo7jnhnXDqsgQ8GagR+DSic=
gitea.cdlsxd.cn/sdk/plugin v1.0.6/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/carlmjohnson/requests v0.24.2 h1:JDakhAmTIKL/qL/1P7Kkc2INGBJIkIFP6xUeUmPzLso=
github.com/carlmjohnson/requests v0.24.2/go.mod h1:duYA/jDnyZ6f3xbcF5PpZ9N8clgopubP2nK5i6MVMhU=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-kratos/kratos/v2 v2.8.2 h1:EsEA7AmPQ2YQQ0FZrDWO2HgBNqeWM8z/mWKzS5UkQaQ=
github.com/go-kratos/kratos/v2 v2.8.2/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -30,10 +32,6 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
@ -47,11 +45,9 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
@ -68,8 +64,7 @@ google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -2,17 +2,13 @@ package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/carlmjohnson/requests"
"plugins/alipay_cpn/internal/po"
)
// 插件通信信息,若不对应则会报错panic
// https://opendocs.alipay.com/open/282/105997?pathHash=3f0dc947
// https://www.yuque.com/xv2p76/manual/wpg9cl
// 数字分行用户账号查询接口
// https://opendocs.alipay.com/pre-open/9128e8f4_alipay.user.dtbankcust.account.query?pathHash=1118bfe2
const (
Tag = "alipay_cpn"
Version = 1
@ -29,101 +25,63 @@ const (
type AlipayCpnService struct{}
func (s *AlipayCpnService) Order(ctx context.Context, request *proto.OrderRequest) (resp2 *proto.OrderResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
func (s *AlipayCpnService) Order(ctx context.Context, request *proto.OrderRequest) (*proto.OrderResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
poReq, err := orderReq(request.Order, request.Product)
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
}
param, err := c.paramReq(poReq, orderMethod)
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
}
uv, err := req(c, param)
if err != nil {
return nil, err
}
_, bodyBytes, err := Post(ctx, uv)
param, err := c.paramReq(poReq, orderMethod)
if err != nil {
return nil, err
}
uv, err := req(c, param)
if err != nil {
return nil, err
}
var response *po.OrderResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
if err = requests.URL(baseUri).Post().Params(uv).ToJSON(&response).Fetch(ctx); err != nil {
return nil, err
}
return orderResp(request, response), nil
}
func (s *AlipayCpnService) Query(ctx context.Context, request *proto.QueryRequest) (resp2 *proto.QueryResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
func (s *AlipayCpnService) Query(ctx context.Context, request *proto.QueryRequest) (*proto.QueryResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
poReq, err := queryReq(request.Order)
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
return nil, err
}
param, err := c.paramReq(poReq, queryMethod)
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
return nil, err
}
uv, err := req(c, param)
if err != nil {
return nil, err
}
_, bodyBytes, err := Post(ctx, uv)
if err != nil {
return nil, err
}
var response po.QueryResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
if err = requests.URL(baseUri).Post().BodyForm(uv).ToJSON(&response).Fetch(ctx); err != nil {
return nil, fmt.Errorf("请求异常msg:" + err.Error())
}
return queryResp(request, response), nil
}
func (s *AlipayCpnService) Notify(_ context.Context, request *proto.NotifyRequest) (resp2 *proto.NotifyResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
func (s *AlipayCpnService) Notify(_ context.Context, request *proto.NotifyRequest) (*proto.NotifyResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
n := notifyReq(request)
b, err := Verify(n, c.Npk)
@ -131,36 +89,7 @@ func (s *AlipayCpnService) Notify(_ context.Context, request *proto.NotifyReques
return nil, err
}
if !b {
return nil, proto.ErrorSignFail("验签失败")
return nil, fmt.Errorf("验签失败")
}
return notifyResp(n)
}
func (s *AlipayCpnService) QueryAccount(ctx context.Context) error {
c, err := transConfig([]byte(`{"npk": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5sfDbw6kkHXeOSfr7RbrbkV7xHlNE8c5eXNI3ri+UCQLka9aehlnLhI2M1DkzSWFXvHuo5vHlC6set5j25nv+4AKevY1TgAPTMZQjNA3EMXinvBTZVCZnG6Cc3BN5jvAnt31NyTE9JsR5shFL9O6WTFED/WGUaM95kJfVeA1CwWkXUVV2kd1qU5KPAIHKMWsWiu9mSOBVZGd7NWwqfrZq33tiHmqYQ5LkS2JIno3qXukKm/2HIL+Ri1lw7s1c/xE6XalKPiroojK6zLLtrLa3LEGN833NWASrdGi4moYhAPHPZnhwsMr9FfUEsGSvwqdJlAbCtnIdbx3a6mV+sBRxQIDAQAB", "prk": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2cF2AxAJP/cQJGPcvzJ5Tt9AQnSzpncTo0sOsge07E9WOi8zIP772lm+ar0cjX9jx8Pf1fJ0hJuzqcsSikC/ZeFWNVmmG4crnwb1NzS/3Ymb22SVKqAjUyjgUkaMQEBRSBAYSTbzwLi9ziggiSvgaIZvUnpQWStcYzhPG91aF2q968vKMd3670b0WwCWSMmWW7dezyqMVikigJCvhSOBfC9A66MciiwtATLpiHBAsRr6IZQ6o9JeH7TPwwLW45QMOXR5VG0xuNj5/8dQmhZGtkckhYEC0v2DrjKkORdfbm4XcvsBsnVhcIVcDu1YkDZHYvhH45r7fUqYfKbgUHH9dAgMBAAECggEBAJUtundxT4bDNYpO3Mk8IvbH5h2/uBrrRDkxJomn0Ckvpb+itvfDkE4oYuf0jVHyrP3hEZCtrfqOzslCwWFAT7Aewq4Iy89cUEND25yDvMYns5QDqYercVU2abcAA3s9+q+51JEg+e7XhTuP0ejgrpEHjFnGtcaoucvxrqBSXXCjJu7l/R/O4gSctTKytTrkZ2vcbaUjVSsDIHE0qtzYinpHlUz8yzxTyhbI2ZHiLiZt3bQVbfPjKiu9H1VLcg4Ty38aP6g1c+nc0aanKiJSjlFWrka2dKB8EySYzt1yqSXHtsFc5BI7lopMTXlQ3f7eZljFt+/QjoGdD3LkRLGsZM0CgYEA74fZQjMeqqGGKxRH1vHkVBQ293VcLZxMh/62VROhWR2JU+UFYmP4zjtu+jnCMAb03kXFoyEJtEShcPoJmW/X5jIR7ukQYYTmlognPOHt4j8n1LP3AxF6m1qhfCPKjX+cbFoYebJtmR4u/pGQQcz6VgsaVTmPLEvBlJaW+C2fBWsCgYEAwvuanPWxHqWhUzGRXQWBOZC4tnS0R43xmnNOPjX0py/X/PwfDvYiMbuV9qRHoHu1dXQ/IfURpOJLp/ibqhTd7TRk6TwVtpnCZ4XJaXZ4wVnIFuH6d78nbEsQODtOwC82OscXvC+foymouM+7HfjXFYh597y+6f3khPQGoTP2+FcCgYBiffWz5tlBSFGm/RvdInHEsNs5PaS9jHtboDzRFtVNk3uYZ5+jKYBxLLchZQaYQQQHMaFmxnr7CQ1pKycvVHHuUcBWALefoco/M5UOz8fFNDrol2eGd3V20/l61iy/qi1j0FgoElpGj8a7Hpe2ttZo8v/M83IPwMbElndVbUzqKQKBgQCSvNlR8JEYWFv9iRs1n1PuJ5zrKw8VnpxJuScUKHTzcU0iBfe1wcRgGyUz1dRV0WPxfceZXZ6msOtxAObdbBDMWBbtdBt/LV4hnIzEgrVEBbZQikZ94ezgUell+DQ+7Ajq2TRimpCQMWTZmPPgDYqImQn5SCGBZWKaMC/hF4NwxwKBgASRF+xP/NUxj3G3TjRHMskFTgtOvzCChZgRXRuvIEd4zEZ1Y29sPSuTHFnJPVPfMDeGmcwPPVDGpMIumRXKJAO6hC9BtnivDnqjtCh16fzoimBeBlaGgg4KWfXT+7BTAPFzlKg4diHoa4Adc4JC+r9Xh++VAMUc3zvH4LWB+Oen", "app_id": "2021004125622196"}`))
if err != nil {
return err
}
poReq := &po.QueryAccount{PhoneId: "18666173766"}
param, err := c.paramReq(poReq, "alipay.user.dtbankcust.account.query")
if err != nil {
return proto.ErrorParamFail(err.Error())
}
uv, err := req(c, param)
if err != nil {
return err
}
_, bodyBytes, err := Post(ctx, uv)
if err != nil {
return err
}
fmt.Printf("response: %+v\n", string(bodyBytes))
return nil
return notifyResp(n), nil
}

View File

@ -2,7 +2,7 @@ package internal
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/stretchr/testify/assert"
@ -12,18 +12,13 @@ import (
var server = &AlipayCpnService{}
func config() []byte {
// fjxw
return []byte(`{"npk": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5sfDbw6kkHXeOSfr7RbrbkV7xHlNE8c5eXNI3ri+UCQLka9aehlnLhI2M1DkzSWFXvHuo5vHlC6set5j25nv+4AKevY1TgAPTMZQjNA3EMXinvBTZVCZnG6Cc3BN5jvAnt31NyTE9JsR5shFL9O6WTFED/WGUaM95kJfVeA1CwWkXUVV2kd1qU5KPAIHKMWsWiu9mSOBVZGd7NWwqfrZq33tiHmqYQ5LkS2JIno3qXukKm/2HIL+Ri1lw7s1c/xE6XalKPiroojK6zLLtrLa3LEGN833NWASrdGi4moYhAPHPZnhwsMr9FfUEsGSvwqdJlAbCtnIdbx3a6mV+sBRxQIDAQAB", "prk": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2cF2AxAJP/cQJGPcvzJ5Tt9AQnSzpncTo0sOsge07E9WOi8zIP772lm+ar0cjX9jx8Pf1fJ0hJuzqcsSikC/ZeFWNVmmG4crnwb1NzS/3Ymb22SVKqAjUyjgUkaMQEBRSBAYSTbzwLi9ziggiSvgaIZvUnpQWStcYzhPG91aF2q968vKMd3670b0WwCWSMmWW7dezyqMVikigJCvhSOBfC9A66MciiwtATLpiHBAsRr6IZQ6o9JeH7TPwwLW45QMOXR5VG0xuNj5/8dQmhZGtkckhYEC0v2DrjKkORdfbm4XcvsBsnVhcIVcDu1YkDZHYvhH45r7fUqYfKbgUHH9dAgMBAAECggEBAJUtundxT4bDNYpO3Mk8IvbH5h2/uBrrRDkxJomn0Ckvpb+itvfDkE4oYuf0jVHyrP3hEZCtrfqOzslCwWFAT7Aewq4Iy89cUEND25yDvMYns5QDqYercVU2abcAA3s9+q+51JEg+e7XhTuP0ejgrpEHjFnGtcaoucvxrqBSXXCjJu7l/R/O4gSctTKytTrkZ2vcbaUjVSsDIHE0qtzYinpHlUz8yzxTyhbI2ZHiLiZt3bQVbfPjKiu9H1VLcg4Ty38aP6g1c+nc0aanKiJSjlFWrka2dKB8EySYzt1yqSXHtsFc5BI7lopMTXlQ3f7eZljFt+/QjoGdD3LkRLGsZM0CgYEA74fZQjMeqqGGKxRH1vHkVBQ293VcLZxMh/62VROhWR2JU+UFYmP4zjtu+jnCMAb03kXFoyEJtEShcPoJmW/X5jIR7ukQYYTmlognPOHt4j8n1LP3AxF6m1qhfCPKjX+cbFoYebJtmR4u/pGQQcz6VgsaVTmPLEvBlJaW+C2fBWsCgYEAwvuanPWxHqWhUzGRXQWBOZC4tnS0R43xmnNOPjX0py/X/PwfDvYiMbuV9qRHoHu1dXQ/IfURpOJLp/ibqhTd7TRk6TwVtpnCZ4XJaXZ4wVnIFuH6d78nbEsQODtOwC82OscXvC+foymouM+7HfjXFYh597y+6f3khPQGoTP2+FcCgYBiffWz5tlBSFGm/RvdInHEsNs5PaS9jHtboDzRFtVNk3uYZ5+jKYBxLLchZQaYQQQHMaFmxnr7CQ1pKycvVHHuUcBWALefoco/M5UOz8fFNDrol2eGd3V20/l61iy/qi1j0FgoElpGj8a7Hpe2ttZo8v/M83IPwMbElndVbUzqKQKBgQCSvNlR8JEYWFv9iRs1n1PuJ5zrKw8VnpxJuScUKHTzcU0iBfe1wcRgGyUz1dRV0WPxfceZXZ6msOtxAObdbBDMWBbtdBt/LV4hnIzEgrVEBbZQikZ94ezgUell+DQ+7Ajq2TRimpCQMWTZmPPgDYqImQn5SCGBZWKaMC/hF4NwxwKBgASRF+xP/NUxj3G3TjRHMskFTgtOvzCChZgRXRuvIEd4zEZ1Y29sPSuTHFnJPVPfMDeGmcwPPVDGpMIumRXKJAO6hC9BtnivDnqjtCh16fzoimBeBlaGgg4KWfXT+7BTAPFzlKg4diHoa4Adc4JC+r9Xh++VAMUc3zvH4LWB+Oen", "app_id": "2021004125622196"}`)
// lsxd
//return []byte(`{"npk": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjnsbMbY/KYHhUkVbalbtSMbLFcntTEfqlz+t3bskMUOIvPldsW8U+RQymhjw6AGOQooqMvDqbkWk3kZph9mf56fTbRHYTiyAgNp+yzE9p6DQn3I1k7wT1aaWIX0cn+nJ0n427tYskfDNoG9MojvKLJQjVkU7/CejNy7z9j2BWXU/nPqfavwC/EUQmdx+4aI2IwUX4zOAg5ZbvSbnbBaRVnP9lRVEjMIjfl0LuvBqqvHLEwbQTh2UxlHlK9OWaCQmeD3zPCki7GSugwOXkez61wRIrFluWkpGupcAQRQvjkoxesQQy08ikkEbu5NmiQvybfFny46RBrO3/RnqjKqd/QIDAQAB", "prk": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDAtMuB1mGgbZOETHRlfYefThBO2HjiNcg0kprSgPGQR3gJC6uZ6x2njBrGfLpGsEHjGNnHIjepvbeulD49Tht71kEtKPfsw0LgSgbLDjpxwADctCgnniGHuGmVOeuhuL8hO6JxNt4s+0xPoigmgHFltTKth0Ddc5kqthKmxLx3PJ/JqrzlsELzVkzC+gR/0+epTBTzLUpYzlRt3WhHmxEm8Y9XUk6C1naNXaQNrbrXFzTwnW9MxVj/yJaCoCVcifoVic14lWw3w0cdZ1yM6kcAA9vYAlhIJYc0eg2pAQt/mg/mdseourpa0O01d+2287vKOnOivK0UGqEYsw7d+Q9PAgMBAAECggEBAJ8WostViNNAowAdfhorP8OsVppA68sUS2vbfCU8fjB2UApnsAsppxmi+JfUcJPa/wnUgjV7ciiXjwR5Lci+qsHZZ4U4xTSZHqS7eFA6NtWhhRSvRlcunM/G1qNzZavlMSTmbny4p0LLU3yKikr+zp8AsA3CEzK/NDumhE9ZK53i5WWGwOLQI4yaQoD8XkbFxkWf0yKZiqxj+5UXNnaawfY45ZgDRVtd02hLJQnEYLS4PAt/tUW2I3M+2CJzVcJysry6fd56ZE8JLPVomS3ckS4MsUJkefBTbcJ/idkC5CvnRFqzLjOIOGjDKZqppfn99osilEIhq+UrdmeVZwQEO8ECgYEA+cDxSUjgOkhcU6QlD23B3dmNDWTmJ25RPD55mrhfa6tjInvSiotBwZxwmYKDmBTq/tprf82OjSlR7dQ7oFPSbtde/8tyc/bL/1Jz9/oMB+R2ZxDN3wzCR+rpK3n9YlDv3sR8bYN9q70buTd85SWBk4sdCBb2ZxdFFbn+vAg0ANMCgYEAxYaam+F5IY1/012Y9rikd8IqfZueLGOWOqBUflfsh+ov7Q+xvnJvzk/BLqHU9GvTVCmFK7II1lEFT5tfv8rc6w0y0OOkytx6Ymj/qfcwd8LffKfpRucQSE5i1WyxOvXBZQUZKQSxP0pWrELu9JCE0PPINz+pcFATdH4HV4WoShUCgYARsaSmf347QtMfrdpeTHVWpUoD7+kKVBt3PCnbV0KTWzILyF/zJsiC0fV8Oq3eAWVgptLE3ChEQfAm/Ek5I5cd+S4myyARrSNotEu4/sMJcBsfVCZxrfK5q5/XT64u3/MOgjcafx1RJHWpWpADn/OdI1bsMsxEI3bmgiXU5nPgtwKBgHz/dl3I7L/H7KlUZNVU+Xdz3zHHxevvRYVqKq4dc+h60cNiGCgeART9MZISBVJ73as37zc8ng0KDqNW/tu/Q9hXbN32ATYDGkumiIwFip1vK8CV/bDJzmsDd9X5x5o5JR6JgGeNuaEEqR6v6MnJntMgQoZ7+fzCH92rV3gSc/IVAoGBAK0LkOgsU0Hd9ac0m4yCNPAqt02kSVaq0Q31pzO06/8hrA3+TkCfevKzt0dAwwEpjWHIfLKvJGriRgQSIh2eYloQueM6j0KwNsSkSAXWVaNlA5Twy3cMyYGUauioewksU3a/zhZf6QVsvqC0E5ETxhw/S22PfDnv+y4HzrudQIGo", "app_id": "2021003168695204"}`)
// fjxw
//c := &Config{
// AppId: "2021004125622196",
// Prk: "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2cF2AxAJP/cQJGPcvzJ5Tt9AQnSzpncTo0sOsge07E9WOi8zIP772lm+ar0cjX9jx8Pf1fJ0hJuzqcsSikC/ZeFWNVmmG4crnwb1NzS/3Ymb22SVKqAjUyjgUkaMQEBRSBAYSTbzwLi9ziggiSvgaIZvUnpQWStcYzhPG91aF2q968vKMd3670b0WwCWSMmWW7dezyqMVikigJCvhSOBfC9A66MciiwtATLpiHBAsRr6IZQ6o9JeH7TPwwLW45QMOXR5VG0xuNj5/8dQmhZGtkckhYEC0v2DrjKkORdfbm4XcvsBsnVhcIVcDu1YkDZHYvhH45r7fUqYfKbgUHH9dAgMBAAECggEBAJUtundxT4bDNYpO3Mk8IvbH5h2/uBrrRDkxJomn0Ckvpb+itvfDkE4oYuf0jVHyrP3hEZCtrfqOzslCwWFAT7Aewq4Iy89cUEND25yDvMYns5QDqYercVU2abcAA3s9+q+51JEg+e7XhTuP0ejgrpEHjFnGtcaoucvxrqBSXXCjJu7l/R/O4gSctTKytTrkZ2vcbaUjVSsDIHE0qtzYinpHlUz8yzxTyhbI2ZHiLiZt3bQVbfPjKiu9H1VLcg4Ty38aP6g1c+nc0aanKiJSjlFWrka2dKB8EySYzt1yqSXHtsFc5BI7lopMTXlQ3f7eZljFt+/QjoGdD3LkRLGsZM0CgYEA74fZQjMeqqGGKxRH1vHkVBQ293VcLZxMh/62VROhWR2JU+UFYmP4zjtu+jnCMAb03kXFoyEJtEShcPoJmW/X5jIR7ukQYYTmlognPOHt4j8n1LP3AxF6m1qhfCPKjX+cbFoYebJtmR4u/pGQQcz6VgsaVTmPLEvBlJaW+C2fBWsCgYEAwvuanPWxHqWhUzGRXQWBOZC4tnS0R43xmnNOPjX0py/X/PwfDvYiMbuV9qRHoHu1dXQ/IfURpOJLp/ibqhTd7TRk6TwVtpnCZ4XJaXZ4wVnIFuH6d78nbEsQODtOwC82OscXvC+foymouM+7HfjXFYh597y+6f3khPQGoTP2+FcCgYBiffWz5tlBSFGm/RvdInHEsNs5PaS9jHtboDzRFtVNk3uYZ5+jKYBxLLchZQaYQQQHMaFmxnr7CQ1pKycvVHHuUcBWALefoco/M5UOz8fFNDrol2eGd3V20/l61iy/qi1j0FgoElpGj8a7Hpe2ttZo8v/M83IPwMbElndVbUzqKQKBgQCSvNlR8JEYWFv9iRs1n1PuJ5zrKw8VnpxJuScUKHTzcU0iBfe1wcRgGyUz1dRV0WPxfceZXZ6msOtxAObdbBDMWBbtdBt/LV4hnIzEgrVEBbZQikZ94ezgUell+DQ+7Ajq2TRimpCQMWTZmPPgDYqImQn5SCGBZWKaMC/hF4NwxwKBgASRF+xP/NUxj3G3TjRHMskFTgtOvzCChZgRXRuvIEd4zEZ1Y29sPSuTHFnJPVPfMDeGmcwPPVDGpMIumRXKJAO6hC9BtnivDnqjtCh16fzoimBeBlaGgg4KWfXT+7BTAPFzlKg4diHoa4Adc4JC+r9Xh++VAMUc3zvH4LWB+Oen",
// Npk: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5sfDbw6kkHXeOSfr7RbrbkV7xHlNE8c5eXNI3ri+UCQLka9aehlnLhI2M1DkzSWFXvHuo5vHlC6set5j25nv+4AKevY1TgAPTMZQjNA3EMXinvBTZVCZnG6Cc3BN5jvAnt31NyTE9JsR5shFL9O6WTFED/WGUaM95kJfVeA1CwWkXUVV2kd1qU5KPAIHKMWsWiu9mSOBVZGd7NWwqfrZq33tiHmqYQ5LkS2JIno3qXukKm/2HIL+Ri1lw7s1c/xE6XalKPiroojK6zLLtrLa3LEGN833NWASrdGi4moYhAPHPZnhwsMr9FfUEsGSvwqdJlAbCtnIdbx3a6mV+sBRxQIDAQAB",
//}
//marshal, _ := json.Marshal(c)
//return marshal
c := &Config{
AppId: "2021004125622196",
Prk: "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2cF2AxAJP/cQJGPcvzJ5Tt9AQnSzpncTo0sOsge07E9WOi8zIP772lm+ar0cjX9jx8Pf1fJ0hJuzqcsSikC/ZeFWNVmmG4crnwb1NzS/3Ymb22SVKqAjUyjgUkaMQEBRSBAYSTbzwLi9ziggiSvgaIZvUnpQWStcYzhPG91aF2q968vKMd3670b0WwCWSMmWW7dezyqMVikigJCvhSOBfC9A66MciiwtATLpiHBAsRr6IZQ6o9JeH7TPwwLW45QMOXR5VG0xuNj5/8dQmhZGtkckhYEC0v2DrjKkORdfbm4XcvsBsnVhcIVcDu1YkDZHYvhH45r7fUqYfKbgUHH9dAgMBAAECggEBAJUtundxT4bDNYpO3Mk8IvbH5h2/uBrrRDkxJomn0Ckvpb+itvfDkE4oYuf0jVHyrP3hEZCtrfqOzslCwWFAT7Aewq4Iy89cUEND25yDvMYns5QDqYercVU2abcAA3s9+q+51JEg+e7XhTuP0ejgrpEHjFnGtcaoucvxrqBSXXCjJu7l/R/O4gSctTKytTrkZ2vcbaUjVSsDIHE0qtzYinpHlUz8yzxTyhbI2ZHiLiZt3bQVbfPjKiu9H1VLcg4Ty38aP6g1c+nc0aanKiJSjlFWrka2dKB8EySYzt1yqSXHtsFc5BI7lopMTXlQ3f7eZljFt+/QjoGdD3LkRLGsZM0CgYEA74fZQjMeqqGGKxRH1vHkVBQ293VcLZxMh/62VROhWR2JU+UFYmP4zjtu+jnCMAb03kXFoyEJtEShcPoJmW/X5jIR7ukQYYTmlognPOHt4j8n1LP3AxF6m1qhfCPKjX+cbFoYebJtmR4u/pGQQcz6VgsaVTmPLEvBlJaW+C2fBWsCgYEAwvuanPWxHqWhUzGRXQWBOZC4tnS0R43xmnNOPjX0py/X/PwfDvYiMbuV9qRHoHu1dXQ/IfURpOJLp/ibqhTd7TRk6TwVtpnCZ4XJaXZ4wVnIFuH6d78nbEsQODtOwC82OscXvC+foymouM+7HfjXFYh597y+6f3khPQGoTP2+FcCgYBiffWz5tlBSFGm/RvdInHEsNs5PaS9jHtboDzRFtVNk3uYZ5+jKYBxLLchZQaYQQQHMaFmxnr7CQ1pKycvVHHuUcBWALefoco/M5UOz8fFNDrol2eGd3V20/l61iy/qi1j0FgoElpGj8a7Hpe2ttZo8v/M83IPwMbElndVbUzqKQKBgQCSvNlR8JEYWFv9iRs1n1PuJ5zrKw8VnpxJuScUKHTzcU0iBfe1wcRgGyUz1dRV0WPxfceZXZ6msOtxAObdbBDMWBbtdBt/LV4hnIzEgrVEBbZQikZ94ezgUell+DQ+7Ajq2TRimpCQMWTZmPPgDYqImQn5SCGBZWKaMC/hF4NwxwKBgASRF+xP/NUxj3G3TjRHMskFTgtOvzCChZgRXRuvIEd4zEZ1Y29sPSuTHFnJPVPfMDeGmcwPPVDGpMIumRXKJAO6hC9BtnivDnqjtCh16fzoimBeBlaGgg4KWfXT+7BTAPFzlKg4diHoa4Adc4JC+r9Xh++VAMUc3zvH4LWB+Oen",
Npk: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5sfDbw6kkHXeOSfr7RbrbkV7xHlNE8c5eXNI3ri+UCQLka9aehlnLhI2M1DkzSWFXvHuo5vHlC6set5j25nv+4AKevY1TgAPTMZQjNA3EMXinvBTZVCZnG6Cc3BN5jvAnt31NyTE9JsR5shFL9O6WTFED/WGUaM95kJfVeA1CwWkXUVV2kd1qU5KPAIHKMWsWiu9mSOBVZGd7NWwqfrZq33tiHmqYQ5LkS2JIno3qXukKm/2HIL+Ri1lw7s1c/xE6XalKPiroojK6zLLtrLa3LEGN833NWASrdGi4moYhAPHPZnhwsMr9FfUEsGSvwqdJlAbCtnIdbx3a6mV+sBRxQIDAQAB",
}
marshal, _ := json.Marshal(c)
return marshal
}
func TestConfig(t *testing.T) {
@ -35,16 +30,15 @@ func TestConfig(t *testing.T) {
}
func TestOrder(t *testing.T) {
// 5Y8JyDe7GDmxxpNa
request := &proto.OrderRequest{
Config: config(),
Order: &proto.OrderRequest_Order{
OrderNo: "202507291411243510013587",
Account: "18043057911",
Extra: []byte(`{"logon_id":"18043057911"}`),
OrderNo: "240403164049635931",
Account: "",
Extra: []byte(`{"logon_id":"13100720242"}`),
},
Product: &proto.OrderRequest_Product{
ProductNo: "ACT673CCV03194025",
ProductNo: "3102024032977191_11",
Extra: []byte(`{}`),
},
}
@ -63,19 +57,12 @@ func TestQuery(t *testing.T) {
request := &proto.QueryRequest{
Config: config(),
Order: &proto.QueryRequest_Order{
OrderNo: "",
TradeNo: "20251221223651ACT04343CCV00699079851",
Account: "043k0Dp2sNW5SuNidc3UjdrfKvnysMYE7AiA0m0lxEdPu8c",
Extra: []byte(`{"phone_id":"","logon_id":"","activity_id":"ACT907CCV03800305"}`),
OrderNo: "240403180614988314_80",
TradeNo: "",
Account: "",
Extra: []byte(`{"phone_id":"","logon_id":"13691105465","activity_id":"ACT873CCV02108400"}`),
},
}
// lsxd
//{"extra": "eyJsb2dvbl9pZCI6IjU4Mzk4OTAyMEBxcS5jb20ifQ==", "account": "583989020@qq.com", "order_no": "202512012022000330010220", "quantity": 1}
//{"product": "product_no:\"ACT532CCV02911198\"", "trade_no": "202512012022000330010220"}
// fjxw
//{"extra": "eyJsb2dvbl9pZCI6IjEzNzA2NTE0ODc4In0=", "account": "13706514878", "order_no": "202601050921400620016427", "quantity": 1}
//{"product": "product_no:\"ACT773CCV03855459\"", "trade_no": "202601050921400620016427"}
// Insufficient Permissions IP白名单问题
t.Run("TestQuery", func(t *testing.T) {
got, err := server.Query(context.Background(), request)
if err != nil {
@ -105,23 +92,3 @@ func TestNotify(t *testing.T) {
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})
}
func TestBytes(t *testing.T) {
// Base64 编码的字节数据
//byteData := []byte("eyJhbGlwYXlfdXNlcl9kdGJhbmtjdXN0X2NoYW5uZWx2b3VjaGVyX3NlbmRfcmVzcG9uc2UiOnsiY29kZSI6IjQwMDA0IiwibXNnIjoiQnVzaW5lc3MgRmFpbGVkIiwic3ViX2NvZGUiOiJCVURHRVRfTk9UX0VOT1VHSCIsInN1Yl9tc2ciOiLmtLvliqjpooTnrpfkuI3otrMiLCJhY3Rpdml0eV9vcmRlcl9pZCI6IiIsImFjdGl2aXR5X2lkIjoiIiwic2VuZF9zdGF0dXMiOiJGQUlMRUQiLCJkaXNjb3VudF90eXBlIjoiIiwiZGlzY291bnRfdGhyZXNob2xkX2FtdCI6MCwiZGlzY291bnRfdmFsdWUiOjAsInNlbmRfYW1vdW50IjowLCJ2b3VjaGVyX2lkIjoiIn19")
byteData := []byte("eyJsb2dvbl9pZCI6IjE4MDQzMDU3OTExIn0=")
// 解码 Base64
decodedData, err := base64.StdEncoding.DecodeString(string(byteData))
if err != nil {
fmt.Println("解码错误:", err)
return
}
// 将解码后的数据转为字符串
str := string(decodedData)
// 输出结果
fmt.Println(str)
}
func TestQueryAccount(t *testing.T) {
server.QueryAccount(context.Background())
}

View File

@ -3,7 +3,6 @@ package po
import (
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/go-playground/validator/v10"
)
@ -32,7 +31,7 @@ func (req *OrderReq) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorParamFail(fmt.Sprintf("参数有误: %v", err))
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
@ -47,7 +46,7 @@ func (req *QueryReq) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorParamFail(fmt.Sprintf("参数有误: %v", err))
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
@ -62,7 +61,7 @@ func (req *Notify) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorParamFail(fmt.Sprintf("参数有误: %v", err))
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
@ -72,22 +71,3 @@ func (req *Notify) ToJson() []byte {
b, _ := json.Marshal(req)
return b
}
type QueryAccount struct {
PhoneId string `json:"phone_id" validate:"required"`
}
func (req *QueryAccount) ToJson() []byte {
b, _ := json.Marshal(req)
return b
}
func (req *QueryAccount) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorParamFail(fmt.Sprintf("参数有误: %v", err))
}
}
return nil
}

View File

@ -6,7 +6,6 @@ import (
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/go-playground/validator/v10"
"html"
"net/http"
"plugins/alipay_cpn/internal/po"
"plugins/alipay_cpn/internal/vo"
"time"
@ -18,11 +17,11 @@ type Config struct {
Npk string `validate:"required" json:"npk"` // 回调公钥
}
func (c *Config) validate() error {
func (c *Config) Validate() error {
err := validator.New().Struct(c)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
return fmt.Errorf("配置有误:" + err.Error())
}
}
return nil
@ -32,9 +31,9 @@ func transConfig(config []byte) (*Config, error) {
var c Config
err := json.Unmarshal(config, &c)
if err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("配置参数解析失败: %v", err))
return nil, err
}
if err = c.validate(); err != nil {
if err = c.Validate(); err != nil {
return nil, err
}
return &c, nil
@ -63,13 +62,12 @@ func orderReq(order *proto.OrderRequest_Order, product *proto.OrderRequest_Produ
PhoneId string `json:"phone_id"`
}
var extra Extra
if order.Extra != nil {
if err := json.Unmarshal(order.Extra, &extra); err != nil {
return nil, proto.ErrorParamFail(fmt.Sprintf("order拓展参数转换有误: %v", err))
err := json.Unmarshal(order.Extra, &extra)
if err != nil {
return nil, fmt.Errorf("order拓展参数 json unmarshal error: %v", err)
}
}
o := &po.OrderReq{
UserId: order.Account,
LogonId: extra.LogonId,
@ -103,7 +101,7 @@ func queryReq(in *proto.QueryRequest_Order) (*po.QueryReq, error) {
if in.Extra != nil {
err := json.Unmarshal(in.Extra, &extra)
if err != nil {
return nil, proto.ErrorParamFail(fmt.Sprintf("order拓展参数转换有误: %v", err))
return nil, fmt.Errorf("order拓展参数 json unmarshal error: %v", err)
}
}
return &po.QueryReq{
@ -136,11 +134,10 @@ func notifyReq(in *proto.NotifyRequest) *po.Notify {
return n
}
func notifyResp(n *po.Notify) (*proto.NotifyResponse, error) {
func notifyResp(n *po.Notify) *proto.NotifyResponse {
var b po.NotifyBizContent
_ = json.Unmarshal([]byte(n.BizContent), &b)
pb := &proto.NotifyResponse{
return &proto.NotifyResponse{
Result: &proto.Result{
Status: b.BizType.GetOrderStatus(),
OrderNo: b.OrderID,
@ -149,15 +146,4 @@ func notifyResp(n *po.Notify) (*proto.NotifyResponse, error) {
},
Return: "success",
}
headers := make(http.Header)
headers.Set("Content-Type", "text/plain")
headersBytes, err := json.Marshal(headers)
if err != nil {
return nil, err
}
pb.Headers = string(headersBytes)
return pb, nil
}

View File

@ -1,7 +1,6 @@
package internal
import (
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
@ -9,13 +8,11 @@ import (
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"gitea.cdlsxd.cn/sdk/plugin/utils"
"net/http"
"net/url"
"plugins/alipay_cpn/internal/po"
"plugins/utils/request"
"strings"
)
@ -36,39 +33,28 @@ func req(config *Config, req *po.Param) (url.Values, error) {
s := strings.TrimRight(strToBeSigned.String(), "&")
sign, err := Sign(s, []byte(utils.NewPrivate().Build(config.Prk)))
if err != nil {
return nil, proto.ErrorSignFail(err.Error())
return nil, err
}
uv.Set("sign", sign)
return uv, nil
}
func Post(ctx context.Context, uv url.Values) (http.Header, []byte, error) {
h := http.Header{
"Content-Type": []string{"application/x-www-form-urlencoded"},
}
respHeader, respBody, err := request.Post(ctx, baseUri+"?"+uv.Encode(), nil, request.WithHeaders(h))
if err != nil {
return nil, nil, proto.ErrorRequestFail(err.Error())
}
return respHeader, respBody, nil
}
func Sign(data string, privateKeyPEM []byte) (string, error) {
block, _ := pem.Decode(privateKeyPEM)
if block == nil {
return "", proto.ErrorSignFail("failed to parse PEM block containing the private key")
return "", errors.New("failed to parse PEM block containing the private key")
}
privyKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return "", proto.ErrorSignFail(fmt.Sprintf("failed to parse DER encoded private key: %v", err))
return "", fmt.Errorf("failed to parse DER encoded private key: %v", err)
}
hashed := sha256.Sum256([]byte(data))
signature, err := rsa.SignPKCS1v15(rand.Reader, privyKey.(*rsa.PrivateKey), crypto.SHA256, hashed[:])
if err != nil {
return "", proto.ErrorSignFail(fmt.Sprintf("failed to sign:%v", err))
return "", fmt.Errorf("failed to sign: %v", err)
}
return base64.StdEncoding.EncodeToString(signature), nil
@ -92,12 +78,7 @@ func Verify(n *po.Notify, publicKeyPEM string) (bool, error) {
strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
}
s := strings.TrimRight(strToBeSigned.String(), "&")
b, err := check(s, n.Sign, []byte(utils.NewPublic().Build(publicKeyPEM)))
if err != nil {
return false, proto.ErrorSignFail(err.Error())
}
return b, nil
return check(s, n.Sign, []byte(utils.NewPublic().Build(publicKeyPEM)))
}
func check(data, signature string, publicKeyPEM []byte) (bool, error) {

View File

@ -5,10 +5,11 @@ go 1.22.2
replace plugins/utils => ../../utils
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.27
gitea.cdlsxd.cn/sdk/plugin v1.0.6
github.com/carlmjohnson/requests v0.24.2
github.com/go-playground/validator/v10 v10.22.0
github.com/hashicorp/go-plugin v1.6.1
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.9.0
plugins/utils v0.0.0-00010101000000-000000000000
)
@ -16,7 +17,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect

View File

@ -1,15 +1,17 @@
gitea.cdlsxd.cn/sdk/plugin v1.0.27 h1:ocp+3n8qLlPm+yI0VpMXFWfC8gsdDrKxEowuD58T6CE=
gitea.cdlsxd.cn/sdk/plugin v1.0.27/go.mod h1:4fLMp/xB9GEBa3nJi62kXpHh7wnb9Lwjf0I8Vjaasx0=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca h1:snL161P7OynMA8hRVMLDjwnzZA2Q4mePg/iT/dyIfzA=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
gitea.cdlsxd.cn/sdk/plugin v1.0.6 h1:fcQrgdRT4zVmxqK8rsB4Oo7jnhnXDqsgQ8GagR+DSic=
gitea.cdlsxd.cn/sdk/plugin v1.0.6/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/carlmjohnson/requests v0.24.2 h1:JDakhAmTIKL/qL/1P7Kkc2INGBJIkIFP6xUeUmPzLso=
github.com/carlmjohnson/requests v0.24.2/go.mod h1:duYA/jDnyZ6f3xbcF5PpZ9N8clgopubP2nK5i6MVMhU=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-kratos/kratos/v2 v2.8.2 h1:EsEA7AmPQ2YQQ0FZrDWO2HgBNqeWM8z/mWKzS5UkQaQ=
github.com/go-kratos/kratos/v2 v2.8.2/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -30,10 +32,6 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@ -50,11 +48,9 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
@ -73,8 +69,7 @@ google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -2,9 +2,9 @@ package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/carlmjohnson/requests"
"plugins/alipay_redpack/internal/po"
"plugins/utils/alipay"
)
@ -27,111 +27,75 @@ const (
type AlipayRedPackService struct{}
func (s *AlipayRedPackService) Order(ctx context.Context, request *proto.OrderRequest) (resp2 *proto.OrderResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
func (s *AlipayRedPackService) Order(ctx context.Context, request *proto.OrderRequest) (*proto.OrderResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
poReq, err := orderReq(request.Order, request.Product)
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
return nil, err
}
param, err := c.paramReq(poReq, orderMethod)
if err != nil {
return nil, err
}
uv, err := req(c, param)
if err != nil {
return nil, err
}
bodyBytes, _, err := Post(ctx, uv)
if err != nil {
return nil, err
}
var response *po.OrderResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
if err = requests.URL(baseUri).Post().Params(uv).ToJSON(&response).Fetch(ctx); err != nil {
return nil, err
}
return orderResp(request, response)
return orderResp(request, response), nil
}
func (s *AlipayRedPackService) Query(ctx context.Context, request *proto.QueryRequest) (resp2 *proto.QueryResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
func (s *AlipayRedPackService) Query(ctx context.Context, request *proto.QueryRequest) (*proto.QueryResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
param, err := c.paramReq(queryReq(request.Order), queryMethod)
poReq, err := queryReq(request.Order)
if err != nil {
return nil, err
}
param, err := c.paramReq(poReq, queryMethod)
if err != nil {
return nil, err
}
uv, err := req(c, param)
if err != nil {
return nil, err
}
bodyBytes, _, err := Post(ctx, uv)
if err != nil {
return nil, err
}
var response po.QueryResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
if err = requests.URL(baseUri).Post().BodyForm(uv).ToJSON(&response).Fetch(ctx); err != nil {
return nil, fmt.Errorf("请求异常msg:" + err.Error())
}
return queryResp(request, response), nil
}
func (s *AlipayRedPackService) Notify(_ context.Context, request *proto.NotifyRequest) (resp2 *proto.NotifyResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
func (s *AlipayRedPackService) Notify(_ context.Context, request *proto.NotifyRequest) (*proto.NotifyResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
n := notifyReq(request)
cert, err := alipay.GetCert(c.AppId)
if err != nil {
return nil, err
}
b, err := Verify(n, cert.PublicKey)
if err != nil {
return nil, proto.ErrorSignFail(err.Error())
return nil, err
}
if !b {
return nil, proto.ErrorSignFail("验签失败")
return nil, fmt.Errorf("验签失败")
}
return notifyResp(n)
return notifyResp(n), nil
}

View File

@ -12,15 +12,9 @@ import (
var server = &AlipayRedPackService{}
func config() []byte {
// 蓝色兄弟主体
//c := &Config{
// AppId: "2021004100663111",
// Prk: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDbA+YuMp4JUVj6rjzgwGKNXWkEMGX/rinqkfyBZ6B6p8EKz8zgA+ypiJLOixD3GyKnUnAzx4waNRZHfdEu+l57kJFtd/ipfwtJ28aTi7TqtqEpqD+UPY4ourt2CuyCFxWsonS6dczqtTvfAVArTdbGJYY+kNNVR3WiXgGUhkUu8N7vEowU00RUQGNdSVMUs4FX+HlU3RnEoRc/xUhPiaLf0Bm/g9wG96kwyg/TZvkNU7PpMVRdXeLrVORn0qThs3VA4dqondF+O12iC1TK4TKYGzFYczGAUsfuurtDyCc2GMoE+hH2FR8U7amQOVuYZFkutTdqaqukWpFQOr8wLeMzAgMBAAECggEAD715/3v3y6ejA3EeQvDQpGRANeLckcGMlaUkRpCSAf6oawSALuFZUt3T3zAzae7zUJ8mHTKMKR4DmeO68utfevXq3bkvj87nmslGvjfeKrgxYPMMjrTV0KuK6XLjiH3sOtn6FaR9s6iOwvovLs2LT/ZGbZyu84QNOjwTVP9JXZQkBgMItdKf+U3H2Cjp7U/qXBt8/9yzVFklp1g1883DAty0lzmT27dJimGVGaPQ8vNxo81+ZUEJAn6GUTk0K/GwJfhPTU8hh8G90n2LTyskoMjGxQe9lXfCcS9DmWawEQL4WTctPrDYlnS/cjCVMS0KXIFuxRNf6qaMYDeywC8BgQKBgQDyf40dvmw34Rrb46+NLayQ9W5CJI/dYeRajpCjoOomq5QYhbXzUCpVfbtByeGMsg3zN58NNsZGhl5SU0GdEdoOlxCk+2Hey2yQYF4ugQm/dTd68Jgqi3yujigAbNYa1ZhL9t3FqouPY1dGiaxl+DFYdSMIrsVFXh2NbrPyqTk5IQKBgQDnNaH7LCcIUqg9H8Tsls+8GLzP2HwF3hdll8asEsF3K3HX6/Zlp4VnnEcIkAxLRL/L0o5akXrmA18ZwfoSguTPXV9va3G2GgIiJHgcytmGtQVvbpFKuPnCXKz+avxnfO0flJqyYEuHr/40jsGbMkk0Kr52/n3ivXZbBUlT+tkt0wKBgB0qLgSnxEgsMJjFl3V5SsncWrhlwU+02Evz3X1wevjPpe4VFr7+ozjI+F5/MztCpt7bj6t9LPeKbYmlLb0ASqN6k6vj9+9ds97hWDJrnoqCRHvqt8JWKFauDi2O6WksyzZHqIB/dG14WyTGpg9VfEnRPLdsnZksKo26BLZol9NBAoGBAMz7oMNljqlzVtLyMo2q2zuhFuySusoc79NTL4FpE3rK2qCbA5V2YvDL/bIau7uTlRNodmrXZgU84fidIE9/Gsq5tp26vVK8Vj3c5Vxpf1dNcCct+MQtoMjvjzP0uBgsCrKf9lLEytHed1ozYnRsrbgBWWF4GTWH0cG6uxsoX5mfAoGAfYXKZdyRU+Su4y4EnDLMXd320ar7PaeuY8aZU7V6UQEsaOj6H3O8JMEOuBrOVEhAP2EkAC8ayargSXTSOkN97pg88agKDwA6jh4N6TKAK8XMft81YPPliVwZMsAUqihSKQBnKZ7ssHHLGWWWp5vfkyb7Y7dIkZcPzB0X3q/jL58=",
//}
// 福建兴旺主体
c := &Config{
AppId: "2021005198621340",
Prk: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCCNMshouy0vs2zswRtyIc9pfHbAPAflQkQKlmes5+fWa9r0lS7IFmOEM4d0Nb/QRU9Hf2ET5fMXuu6Z5CO22jAq8AhYjL73iqH+xspkFV5qcq2z0QDqfafovKpc0Pa9Ko5peBfYmiyOTkElrU0flL/cYTrLExVC+ypr9kNUpuYQHIPMCgxokzyNFuojExB083xTkD5xzUznVu0vvSEVL4ir/Po/buLZoPr5M6se2Cj1Qn04uixEKq0tBuX5ZPyQRnDAFuLxsNBeNh06Hu3uSq2gvuDHx7pSH5QEpqMbvue9Rk36CZ2YuEm0ZQk1NPyH+wefLDfzouZh9jORDNz/8M9AgMBAAECggEASPEeYseM52yEfAjrkDzoVobiSJfr7SVmoGG5NwjVgUWS2kpIIVRsFwQfE+DssK5bIZPtc/PKzDjYweJGv2D2/Ai7Z3hFq4e4N9laTByJZdDSuTq/lXXfrWpxmq/x8XVi70yXt5e/oygfYwyykqe6A1Qq7DwyvuohOKZEaAxBEjchZxHBMR0VJz0fifByMOKiyO+5EAdsvigcyBnU5qUZ975lQfyxrEzAWCkYXVThVliP/10sqgp2wMjMCc5PbVA0LpYAmwwLk7z33ZFq8lntbZb0AxPzPVBTgTUaQ7lyk1uLKG09vhZE59x1YMNlNjONLv6EibLdhbXN37xu0TOCWQKBgQD6IDrV3Af3mEBR1chI+K+vmgsXRw8g+T+WLDzZ+L3VZzCAGb+vH5+HnGCWMMJU/jLz6iQrLNKrA7c9zfzb+OaCVyASZ1yum4Q9KbjRCGRNX2CjRKqNboDD8iEOAD75A0KnrILw4B4yp0/tRjgkxA0IBVjLKE9UIlwy3pbMHNMAnwKBgQCFQ5mnt6qpcPnAK0PIJZ2koFkOup9yFcRZYiO2cLA3Vor6etf/KmzSlDPgTVS62fU2sHBdmp33lIqzoHlhznzRTeC1sA+4+J8Ur8uFAWGrEdl+2GM0KRqIDetVO8PcFbFPcpjkAnx/w+2cPAdI7vNWGZasVDjdyoOpd7qUFKbiowKBgFgDgVCBlVgdffyN4mUzBPcgmGh/e6ZZrnNUa7EzovD1144sAVRJJ2SbtS0ESU2JCTrlGAcNRbAhpZgc5K4u3kXdDI8NSgKy/IxdIwiDEHBdcG7jGbScvri0eccodQBAknLX9Fd4NYgTcb+HZvgg+IjrALy0GX38fT71LlDyj5j3AoGANFo0OPEseFhLpTTZ8fzlC1F66xBhpx3p/vtQ8FEgyPyUqIYt6K4wBiH2pz159dW+QXfbDMq7GBIg0yRNyyClZExl5Lp/0rjJNgVBvMSoK95dRTmY71rec2iUdO1/qixTddSdP6XCZyb/Z4sliX9l1QNEb/q+RFNUmfTRNW9PiGUCgYEAzQfDCf8CYrT0yd9EI9DgJtitRUOQfTarptxmQcayr14q45jJVUQKrJ19zQQT9X4PHl4W93XQzWMyWrSgcINR7pF/3yUF6/3U6iniwFlXAHtskM0FU6HffmSLVXYALdYBRHP2OGrx3ET5dYdX0Nx8vEpze3Q+gb8KfcIejx4lr5Q=",
AppId: "2021004100663111",
Prk: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDbA+YuMp4JUVj6rjzgwGKNXWkEMGX/rinqkfyBZ6B6p8EKz8zgA+ypiJLOixD3GyKnUnAzx4waNRZHfdEu+l57kJFtd/ipfwtJ28aTi7TqtqEpqD+UPY4ourt2CuyCFxWsonS6dczqtTvfAVArTdbGJYY+kNNVR3WiXgGUhkUu8N7vEowU00RUQGNdSVMUs4FX+HlU3RnEoRc/xUhPiaLf0Bm/g9wG96kwyg/TZvkNU7PpMVRdXeLrVORn0qThs3VA4dqondF+O12iC1TK4TKYGzFYczGAUsfuurtDyCc2GMoE+hH2FR8U7amQOVuYZFkutTdqaqukWpFQOr8wLeMzAgMBAAECggEAD715/3v3y6ejA3EeQvDQpGRANeLckcGMlaUkRpCSAf6oawSALuFZUt3T3zAzae7zUJ8mHTKMKR4DmeO68utfevXq3bkvj87nmslGvjfeKrgxYPMMjrTV0KuK6XLjiH3sOtn6FaR9s6iOwvovLs2LT/ZGbZyu84QNOjwTVP9JXZQkBgMItdKf+U3H2Cjp7U/qXBt8/9yzVFklp1g1883DAty0lzmT27dJimGVGaPQ8vNxo81+ZUEJAn6GUTk0K/GwJfhPTU8hh8G90n2LTyskoMjGxQe9lXfCcS9DmWawEQL4WTctPrDYlnS/cjCVMS0KXIFuxRNf6qaMYDeywC8BgQKBgQDyf40dvmw34Rrb46+NLayQ9W5CJI/dYeRajpCjoOomq5QYhbXzUCpVfbtByeGMsg3zN58NNsZGhl5SU0GdEdoOlxCk+2Hey2yQYF4ugQm/dTd68Jgqi3yujigAbNYa1ZhL9t3FqouPY1dGiaxl+DFYdSMIrsVFXh2NbrPyqTk5IQKBgQDnNaH7LCcIUqg9H8Tsls+8GLzP2HwF3hdll8asEsF3K3HX6/Zlp4VnnEcIkAxLRL/L0o5akXrmA18ZwfoSguTPXV9va3G2GgIiJHgcytmGtQVvbpFKuPnCXKz+avxnfO0flJqyYEuHr/40jsGbMkk0Kr52/n3ivXZbBUlT+tkt0wKBgB0qLgSnxEgsMJjFl3V5SsncWrhlwU+02Evz3X1wevjPpe4VFr7+ozjI+F5/MztCpt7bj6t9LPeKbYmlLb0ASqN6k6vj9+9ds97hWDJrnoqCRHvqt8JWKFauDi2O6WksyzZHqIB/dG14WyTGpg9VfEnRPLdsnZksKo26BLZol9NBAoGBAMz7oMNljqlzVtLyMo2q2zuhFuySusoc79NTL4FpE3rK2qCbA5V2YvDL/bIau7uTlRNodmrXZgU84fidIE9/Gsq5tp26vVK8Vj3c5Vxpf1dNcCct+MQtoMjvjzP0uBgsCrKf9lLEytHed1ozYnRsrbgBWWF4GTWH0cG6uxsoX5mfAoGAfYXKZdyRU+Su4y4EnDLMXd320ar7PaeuY8aZU7V6UQEsaOj6H3O8JMEOuBrOVEhAP2EkAC8ayargSXTSOkN97pg88agKDwA6jh4N6TKAK8XMft81YPPliVwZMsAUqihSKQBnKZ7ssHHLGWWWp5vfkyb7Y7dIkZcPzB0X3q/jL58=",
}
marshal, _ := json.Marshal(c)
return marshal
@ -38,16 +32,16 @@ func TestOrder(t *testing.T) {
request := &proto.OrderRequest{
Config: config(),
Order: &proto.OrderRequest_Order{
OrderNo: "lsxd20250929001",
Account: "18666666666",
OrderNo: "lsxd202406071545141534",
Account: "18512869479",
Quantity: 1,
Amount: 0.01,
Extra: []byte(`{"name":"李子铭"}`),
Extra: []byte(`{"name":"樊波"}`),
},
Product: &proto.OrderRequest_Product{
ProductNo: "",
Price: 0.01,
Extra: []byte(`{"wishing":"测试红包"}`),
Extra: []byte(`{"wishing":"zhufuyu"}`),
},
}
t.Run("TestOrder", func(t *testing.T) {

View File

@ -1,6 +1,7 @@
package po
import (
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"plugins/alipay_redpack/internal/vo"
)
@ -42,6 +43,16 @@ type OrderResp struct {
Sign string `json:"sign"`
}
func (o *OrderResp) GetMsg() string {
if o.Response.Code.IsSuccess() {
return o.Response.Msg
}
return fmt.Sprintf(
"code:[%s],msg:[%s],subCode:[%s],subMsg[%s]",
o.Response.Code, o.Response.Msg, o.Response.SubCode, o.Response.SubMsg,
)
}
func (o *OrderResp) GetOrderStatus() proto.Status {
if o.Response.Code.IsSuccess() {
return proto.Status_ING

View File

@ -6,7 +6,6 @@ import (
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/go-playground/validator/v10"
"html"
"net/http"
"plugins/alipay_redpack/internal/po"
"plugins/alipay_redpack/internal/vo"
"plugins/utils/alipay"
@ -19,11 +18,11 @@ type Config struct {
Prk string `validate:"required" json:"prk"`
}
func (c *Config) validate() error {
func (c *Config) Validate() error {
err := validator.New().Struct(c)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
return fmt.Errorf("配置有误:" + err.Error())
}
}
return nil
@ -33,9 +32,9 @@ func transConfig(config []byte) (*Config, error) {
var c Config
err := json.Unmarshal(config, &c)
if err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("配置参数解析失败: %v", err))
return nil, err
}
if err = c.validate(); err != nil {
if err = c.Validate(); err != nil {
return nil, err
}
return &c, nil
@ -47,7 +46,7 @@ func (c *Config) paramReq(req po.Req, method string) (*po.Param, error) {
}
cert, err := alipay.GetCert(c.AppId)
if err != nil {
return nil, proto.ErrorConfigFail(err.Error())
return nil, err
}
return &po.Param{
AlipayRootCertSn: cert.RootCertSN,
@ -110,31 +109,26 @@ func orderReq(order *proto.OrderRequest_Order, product *proto.OrderRequest_Produ
}, nil
}
func orderResp(request *proto.OrderRequest, resp *po.OrderResp) (*proto.OrderResponse, error) {
if !resp.Response.Code.IsSuccess() {
return nil, proto.ErrorSupplierReturnError(fmt.Sprintf("%s(%s)", resp.Response.SubMsg, resp.Response.SubCode))
}
func orderResp(request *proto.OrderRequest, resp *po.OrderResp) *proto.OrderResponse {
data, _ := json.Marshal(resp)
return &proto.OrderResponse{
Result: &proto.Result{
Status: resp.GetOrderStatus(),
OrderNo: request.Order.OrderNo,
TradeNo: resp.Response.OrderId,
Message: resp.Response.Msg,
Message: resp.GetMsg(),
Data: data,
},
}, nil
}
}
func queryReq(in *proto.QueryRequest_Order) *po.QueryReq {
func queryReq(in *proto.QueryRequest_Order) (*po.QueryReq, error) {
return &po.QueryReq{
OutBizNo: in.OrderNo,
OrderId: in.TradeNo,
ProductCode: "STD_RED_PACKET",
BizScene: "DIRECT_TRANSFER",
}
}, nil
}
func queryResp(request *proto.QueryRequest, resp po.QueryResp) *proto.QueryResponse {
@ -157,11 +151,10 @@ func notifyReq(in *proto.NotifyRequest) *po.Notify {
return n
}
func notifyResp(n *po.Notify) (*proto.NotifyResponse, error) {
func notifyResp(n *po.Notify) *proto.NotifyResponse {
var b po.NotifyBizContent
_ = json.Unmarshal([]byte(n.BizContent), &b)
pb := &proto.NotifyResponse{
return &proto.NotifyResponse{
Result: &proto.Result{
Status: b.Status.GetOrderStatus(),
OrderNo: b.OutBizNo,
@ -170,15 +163,4 @@ func notifyResp(n *po.Notify) (*proto.NotifyResponse, error) {
},
Return: "success",
}
headers := make(http.Header)
headers.Set("Content-Type", "text/plain")
headersBytes, err := json.Marshal(headers)
if err != nil {
return nil, err
}
pb.Headers = string(headersBytes)
return pb, nil
}

View File

@ -1,7 +1,6 @@
package internal
import (
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
@ -11,12 +10,9 @@ import (
"encoding/pem"
"errors"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"gitea.cdlsxd.cn/sdk/plugin/utils"
"net/http"
"net/url"
"plugins/alipay_redpack/internal/po"
"plugins/utils/request"
"strings"
)
@ -35,24 +31,13 @@ func req(config *Config, req *po.Param) (url.Values, error) {
sign, err := Sign(s, []byte(utils.NewPrivate().Build(config.Prk)))
if err != nil {
return nil, proto.ErrorSignFail(err.Error())
return nil, err
}
uv.Set("sign", sign)
return uv, nil
}
func Post(ctx context.Context, uv url.Values) ([]byte, http.Header, error) {
h := http.Header{
"Content-Type": []string{"application/x-www-form-urlencoded"},
}
respHeader, respBody, err := request.Post(ctx, baseUri+"?"+uv.Encode(), nil, request.WithHeaders(h))
if err != nil {
return nil, nil, proto.ErrorRequestFail(err.Error())
}
return respBody, respHeader, nil
}
func Sign(data string, privateKeyPEM []byte) (string, error) {
block, _ := pem.Decode(privateKeyPEM)
if block == nil {

View File

@ -1,7 +0,0 @@
package vo
type SubCode string
func (s SubCode) String() string {
return string(s)
}

View File

@ -1,39 +0,0 @@
module plugins/lsxd_wechat_cpn
go 1.22.2
replace plugins/utils => ../../utils
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.27
github.com/go-playground/validator/v10 v10.22.0
github.com/hashicorp/go-plugin v1.6.1
github.com/stretchr/testify v1.10.0
plugins/utils v1.0.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/hashicorp/go-hclog v0.14.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.10 // indirect
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/grpc v1.64.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -1,75 +0,0 @@
gitea.cdlsxd.cn/sdk/plugin v1.0.27 h1:ocp+3n8qLlPm+yI0VpMXFWfC8gsdDrKxEowuD58T6CE=
gitea.cdlsxd.cn/sdk/plugin v1.0.27/go.mod h1:4fLMp/xB9GEBa3nJi62kXpHh7wnb9Lwjf0I8Vjaasx0=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-kratos/kratos/v2 v2.8.2 h1:EsEA7AmPQ2YQQ0FZrDWO2HgBNqeWM8z/mWKzS5UkQaQ=
github.com/go-kratos/kratos/v2 v2.8.2/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI=
github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,128 +0,0 @@
package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"net/http"
)
const (
Tag = "lsxd_wechat_cpn"
Version = 1
CookieKey = "lsxd_wechat_cpn"
CookieValue = "lsxd_wechat_cpn"
)
const (
orderMethod = "/wechat/coupon/issue" // 支持幂等
queryMethod = "/wechat/coupon/query"
configMethod = "/wechat/coupon/config"
)
// LSXDWeChatCpnService
// 钉钉文档 https://alidocs.dingtalk.com/i/nodes/Y1OQX0akWm6LKDO4iOo1xE0QVGlDd3mE?utm_scene=person_space
type LSXDWeChatCpnService struct{}
func (p *LSXDWeChatCpnService) Order(ctx context.Context, request *proto.OrderRequest) (resp2 *proto.OrderResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
config, err := transConfig(request.Config)
if err != nil {
return nil, err
}
bizContent, err := config.orderReq(request.Order, request.Product)
if err != nil {
return nil, err
}
reqBody, err := bizContent.Json()
if err != nil {
return nil, err
}
bodyBytes, _, err := config.Post(ctx, orderMethod, reqBody)
if err != nil {
return nil, err
}
return orderResp(request.GetOrder(), bodyBytes)
}
func (p *LSXDWeChatCpnService) Query(ctx context.Context, request *proto.QueryRequest) (resp2 *proto.QueryResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
config, err := transConfig(request.Config)
if err != nil {
return nil, err
}
bizContent, err := config.queryReq(request.Order)
if err != nil {
return nil, err
}
reqBody, err := bizContent.Json()
if err != nil {
return nil, err
}
bodyBytes, _, err := config.Post(ctx, queryMethod, reqBody)
if err != nil {
return nil, err
}
return queryResp(request, bodyBytes)
}
func (p *LSXDWeChatCpnService) Notify(ctx context.Context, request *proto.NotifyRequest) (resp2 *proto.NotifyResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
config, err := transConfig(request.Config)
if err != nil {
return nil, err
}
var h http.Header
if err = json.Unmarshal(request.Headers, &h); err != nil {
return nil, proto.ErrorParamFail("header参数解析失败:" + err.Error())
}
if err = config.verify(request.Body, h.Get("Verify")); err != nil {
return nil, err
}
return notifyResp(request.Body)
}
func (p *LSXDWeChatCpnService) Label(ctx context.Context, request *proto.QueryRequest) (resp2 string, respErr error) {
config, err := transConfig(request.Config)
if err != nil {
return "", err
}
bodyBytes, _, err := config.Post(ctx, configMethod, []byte(`{}`))
if err != nil {
return "", err
}
return string(bodyBytes), nil
}

View File

@ -1,280 +0,0 @@
package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/stretchr/testify/assert"
"net/http"
"plugins/utils/request"
"testing"
)
var server = &LSXDWeChatCpnService{}
func config() []byte {
// 测试环境
c := &Config{
BaseUrl: "https://gateway.dev.cdlsxd.cn",
NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/supplier/notify/54",
SignConfig: SignConfig{
Key: "7f8d9b4a2c1e0f568978675443210987",
IV: "a1b2c3d4e5f60708",
},
VerifyConfig: VerifyConfig{
Key: "87654321abcdef0987654321fedcba09",
IV: "0807060504030201",
},
Source: "test",
Label: "lsxdold",
}
// 预发布环境
//c := &Config{
// BaseUrl: "https://wechat-pre.86698.cn",
// NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/supplier/notify/53",
// SignConfig: SignConfig{
// Key: "3e9c1a7f6b2d4e5089f0c8b7a5d1e243",
// IV: "8f4a2c9d1e6b7053",
// },
// VerifyConfig: VerifyConfig{
// Key: "4f9c2e1b7a6d8053e8a1d0c9b2f37645",
// IV: "1f2e3d4c5b6a7988",
// },
// Source: "ymt",
// Label: "lsxdold",
//}
////生产
//c := &Config{
// BaseUrl: "https://wechat.86698.cn",
// NotifyUrl: "https://market.86698.cn/v1/supplier/notify/45",
// SignConfig: SignConfig{
// Key: "3e9c1a7f6b2d4e5089f0c8b7a5d1e243",
// IV: "8f4a2c9d1e6b7053",
// },
// VerifyConfig: VerifyConfig{
// Key: "4f9c2e1b7a6d8053e8a1d0c9b2f37645",
// IV: "1f2e3d4c5b6a7988",
// },
// Source: "ymt",
// Label: "lsxdold",
//}
marshal, _ := json.Marshal(c)
return marshal
}
func Test_Config(t *testing.T) {
t.Run("Test_Config", func(t *testing.T) {
c := config()
t.Logf("%s\n", string(c))
assert.NotEmpty(t, c)
})
}
func Test_Order(t *testing.T) {
req := &proto.OrderRequest{
Config: config(),
Order: &proto.OrderRequest_Order{
OrderNo: "202411141940575304120011",
Account: "oknbq5kTqBqmzvrRqRoLB8V26DCU",
Extra: []byte(`{"app_id":"wx83fd6da8093f55b7","stock_creator_mchid":"1605446142","stock_creator_name":"wx_蓝色兄弟","ymt_order_no":"123456789","ymt_activity_name":"acName"}`),
},
Product: &proto.OrderRequest_Product{
ProductNo: "21406497",
Extra: []byte(`{}`),
},
}
t.Run("Test_Order", func(t *testing.T) {
got, err := server.Order(context.Background(), req)
if err != nil {
t.Errorf("Order() error = %v", err)
return
}
t.Logf("%+v", got)
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})
}
func Test_Query(t *testing.T) {
req := &proto.QueryRequest{
Config: config(),
Order: &proto.QueryRequest_Order{
OrderNo: "202601131847108250010003",
TradeNo: "146156565422",
Account: "oknbq5mAjfgPiCJV028g-ZFDx1WU",
Extra: []byte(`{"app_id":"wx83fd6da8093f55b7"}`),
},
}
t.Run("Test_Query", func(t *testing.T) {
got, err := server.Query(context.Background(), req)
if err != nil {
t.Errorf("Query() error = %v", err)
return
}
t.Logf("%+v \n", got)
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})
}
func Test_Notify(t *testing.T) {
in := &proto.NotifyRequest{
Config: config(),
Queries: []byte(``),
Headers: []byte(`{"Accept-Encoding":["gzip"],"Connection":["close"],"Content-Length":["956"],"Content-Type":["application/json"],"User-Agent":["Go-http-client/1.1"],"Verify":["E8ofjMT6lR/yHYGT6ZS/66W0tTZmg/DeqYuUr7anrwwtKiKwMCHJD3uos1k3rT7DJBnhe9QpIfuBZ62szs56g93LND7W/SldguDIQP8TKAjOrO7jKsfvEm4teTGqi2T97ExHv4H5xcRNsXOUgis2jmUP6uvyx7MsEChpgMAaDTo8KeRAMrew9qjKZb+urfNlSlT9TrJGUQKrI8/kr8h86JXDHCtvhpC3LLLIlS+5wuGGSzOs8Tboy+EBJCsE01S6VjVKqJaGuhJZsRNfkuFvHvJYf285xf5vWuprBdtkONvJJlgTpsxky/vXr+SllLU9oFQUZ+SO6fho3cagDbHF6Jx1SZQZv4U2i96kvH0uL3Wp7gEWy6bpd7EpyEUmJJtH1u63HZOyCDJFLJ/0taFQSqb4nRUPBNdRhgDKtVbg0ds6Sn/1GB2+TEga3xLvruqbPnOP1pRpPeR8c2yv1XlMWPXN46yQ9o24kXnniuD9GKuHqHNn4NR5DhIxLtbxZ75zhiPXGbPPIPLUmvAno1azWX+W9afEHMdHI30oRKVlGGI71o10sFOHLDQ+gzLmurp+DrBByK9o70wGjlBBq2uPapWy1PHKG3k+f7SKgWUyySiMaLHWR6N1GnBtS1uW+QMvds6//8ZPCbAq5BrS4ibxULobmk9nfXEoiAeYVFL66Cv4KJVZLzQGtyaR2N4kjM4BvqLHjJJ2PzXvAfYtixCORviXrjqon6vjz3peLRPyjjiPSIn6mK9z7JFVXRtq25EDWjoSh3OAlUn1Jy0JhFsxfhO07+S8RJJ8+QY4d0igK42bMVek3KAWo6nZebkh1xqdQC7sozRjHNZgee/a9xXO+PNwrf1bxto80N5Ob1KnEB1bC6PRWvyXR8279qnWcI78swbH+Yjbs6j0iHl/KHAMfdrdaDJLKxL4ck4sbyTtCdjaFEHC5ZY3gHuwTY9HC7uvW1spdJECUMIWcM1IwGhpe2EMhQOq1nEoMafCWgS8W43dGMxNE046rMQxmZBU6AHO+7BP3i3w6smH8101mRxfBGdQuZMkOHvnRrIqssT+5Sp7LesGNLyP5AbywhzhKIySOLJSlFodVA7c2uM/+oBdJ/2apSiuijmAD6ZXT/kbxh3FXFywaDQ1Sh6uVY/NmYY5jmI9BSnzFpgccSDH2+1MBmiEit7Vr9JMn1bDMGxA8vbOdKThaiYSbzzGs6tPsWyzHGgo8CtN2oQHINTqW7+g2dcarkP5r170M28tGXX51mIlfG/BOLeDnjkxNMgzPTOL"],"X-Forwarded-For":["39.96.194.80"],"X-Real-Ip":["39.96.194.80"],"X-Remoteaddr":["172.21.0.1"]}`),
Body: []byte(`{"query":{"stock_creator_mchid":"1652465541","stock_id":"21594627","coupon_id":"148861250136","cut_to_message":{},"coupon_name":"微信立减金发放测试","status":"USED","create_time":"2026-01-15T18:23:21+08:00","coupon_type":"NORMAL","available_begin_time":"2026-01-14T00:00:00+08:00","available_end_time":"2026-04-14T23:59:59+08:00","normal_coupon_information":{"coupon_amount":100,"transaction_minimum":101}},"notify":{"stock_creator_mchid":"1652465541","stock_id":"21594627","coupon_id":"148861250136","coupon_name":"微信立减金发放测试","status":"USED","create_time":"2026-01-15T18:23:21+08:00","coupon_type":"NORMAL","available_begin_time":"2026-01-14T00:00:00+08:00","available_end_time":"2026-04-14T23:59:59+08:00","normal_coupon_information":{"coupon_amount":100,"transaction_minimum":101},"consume_information":{"consume_time":"2026-01-15T18:25:22+08:00","consume_mchid":"1532524971","transaction_id":"4200002984202601152635066771"}}}`),
}
t.Run("Test_Notify", func(t *testing.T) {
got, err := server.Notify(context.Background(), in)
if err != nil {
t.Errorf("Notify() error = %v", err)
return
}
t.Logf("TestNotify : %+v \n", got)
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})
}
func Test_Label(t *testing.T) {
req := &proto.QueryRequest{
Config: config(),
}
t.Run("Test_Label", func(t *testing.T) {
got, err := server.Label(context.Background(), req)
if err != nil {
t.Errorf("Label() error = %v", err)
return
}
fmt.Printf("%+v \n", got)
})
}
func Test_AES256CBCEncrypt(t *testing.T) {
plaintext := `{
"query": {
"stock_creator_mchid": "1715349578",
"stock_id": "21408595",
"coupon_id": "146156565422",
"cut_to_message": {
"single_price_max": 0,
"cut_to_price": 0
},
"coupon_name": "贵州银行1.08",
"status": "USED",
"description": "",
"create_time": "2025-12-27T16:29:00+08:00",
"coupon_type": "NORMAL",
"no_cash": false,
"available_begin_time": "2025-12-27T00:00:00+08:00",
"available_end_time": "2026-01-10T23:59:59+08:00",
"singleitem": false,
"normal_coupon_information": {
"coupon_amount": 108,
"transaction_minimum": 990
},
"out_request_no": "",
"available_balance": 0,
"business_type": "",
"discount_msg": null
},
"notify": {
"stock_creator_mchid": "1652465541",
"stock_id": "20886559",
"coupon_id": "146156565422",
"singleitem_discount_off": null,
"discount_to": null,
"coupon_name": "招商银行1元立减金",
"status": "USED",
"description": "",
"create_time": "2025-10-30T09:50:48+08:00",
"coupon_type": "NORMAL",
"no_cash": false,
"available_begin_time": "2025-11-01T00:00:00+08:00",
"available_end_time": "2025-11-30T23:59:59+08:00",
"singleitem": false,
"normal_coupon_information": {
"coupon_amount": 100,
"transaction_minimum": 1800
},
"consume_information": {
"consume_time": "2025-11-24T00:09:50+08:00",
"consume_mchid": "1515540081",
"transaction_id": "4200002932202511240241356500",
"consume_amount": 0,
"goods_detail": null
},
"business_type": ""
}
}`
sign, err := Sign([]byte(plaintext), []byte(`7f8d9b4a2c1e0f568978675443210987`), []byte(`a1b2c3d4e5f60708`))
if err != nil {
t.Errorf("sign() error = %v", err)
return
}
fmt.Printf("sign : %s \n", sign)
}
func Test_WxNotifyRequest(t *testing.T) {
uri := "https://gateway.dev.cdlsxd.cn/yxh5api/v1/supplier/notify/54"
body := []byte(`{"query":{"status": "SENED", "stock_id": "21594627", "coupon_id": "148733945749", "coupon_name": "微信立减金发放测试", "coupon_type": "NORMAL", "create_time": "2026-01-14T18:06:48+08:00", "cut_to_message": {}, "available_end_time": "2026-04-14T23:59:59+08:00", "stock_creator_mchid": "1652465541", "available_begin_time": "2026-01-14T00:00:00+08:00", "normal_coupon_information": {"coupon_amount": 100, "transaction_minimum": 101}},"notify":{"stock_creator_mchid":"1652465541","stock_id":"21594627","coupon_id":"148861250136","coupon_name":"微信立减金发放测试","status":"USED","create_time":"2026-01-15T18:23:21+08:00","coupon_type":"NORMAL","available_begin_time":"2026-01-14T00:00:00+08:00","available_end_time":"2026-04-14T23:59:59+08:00","normal_coupon_information":{"coupon_amount":100,"transaction_minimum":101},"consume_information":{"consume_time":"2026-01-15T18:25:22+08:00","consume_mchid":"1532524971","transaction_id":"4200002984202601152635066771"}}}`)
c, err := transConfig(config())
if err != nil {
t.Errorf("transConfig() error = %v", err)
return
}
v, err := Sign(body, []byte(c.VerifyConfig.Key), []byte(c.VerifyConfig.IV))
if err != nil {
t.Errorf("Sign() error = %v", err)
return
}
hds := fmt.Sprintf(`{"Content-Type":["application/json"],"Verify":["%s"]}`, v)
var headerMap http.Header
if err = json.Unmarshal([]byte(hds), &headerMap); err != nil {
t.Error(fmt.Sprintf("解析 headers 失败: %v", err))
return
}
ctx := context.Background()
respHeader, respBody, err := request.Post(ctx, uri, body, request.WithHeaders(headerMap))
if err != nil {
t.Errorf("请求失败: [%s]-%v", string(respBody), err)
return
}
t.Logf("响应体:%s", string(respBody))
t.Logf("响应头:%v", respHeader)
}
func Test_NotifyRequest(t *testing.T) {
uri := "https://gateway.dev.cdlsxd.cn/yxh5api/v1/supplier/notify/53"
body := []byte(`{"query":{"stock_creator_mchid":"1652465541","stock_id":"21594627","coupon_id":"148861250136","cut_to_message":{},"coupon_name":"微信立减金发放测试","status":"USED","create_time":"2026-01-15T18:23:21+08:00","coupon_type":"NORMAL","available_begin_time":"2026-01-14T00:00:00+08:00","available_end_time":"2026-04-14T23:59:59+08:00","normal_coupon_information":{"coupon_amount":100,"transaction_minimum":101}},"notify":{"stock_creator_mchid":"1652465541","stock_id":"21594627","coupon_id":"148861250136","coupon_name":"微信立减金发放测试","status":"USED","create_time":"2026-01-15T18:23:21+08:00","coupon_type":"NORMAL","available_begin_time":"2026-01-14T00:00:00+08:00","available_end_time":"2026-04-14T23:59:59+08:00","normal_coupon_information":{"coupon_amount":100,"transaction_minimum":101},"consume_information":{"consume_time":"2026-01-15T18:25:22+08:00","consume_mchid":"1532524971","transaction_id":"4200002984202601152635066771"}}}`)
hds := `{"Accept-Encoding":["gzip"],"Connection":["close"],"Content-Length":["956"],"Content-Type":["application/json"],"User-Agent":["Go-http-client/1.1"],"Verify":["E8ofjMT6lR/yHYGT6ZS/66W0tTZmg/DeqYuUr7anrwwtKiKwMCHJD3uos1k3rT7DJBnhe9QpIfuBZ62szs56g93LND7W/SldguDIQP8TKAjOrO7jKsfvEm4teTGqi2T97ExHv4H5xcRNsXOUgis2jmUP6uvyx7MsEChpgMAaDTo8KeRAMrew9qjKZb+urfNlSlT9TrJGUQKrI8/kr8h86JXDHCtvhpC3LLLIlS+5wuGGSzOs8Tboy+EBJCsE01S6VjVKqJaGuhJZsRNfkuFvHvJYf285xf5vWuprBdtkONvJJlgTpsxky/vXr+SllLU9oFQUZ+SO6fho3cagDbHF6Jx1SZQZv4U2i96kvH0uL3Wp7gEWy6bpd7EpyEUmJJtH1u63HZOyCDJFLJ/0taFQSqb4nRUPBNdRhgDKtVbg0ds6Sn/1GB2+TEga3xLvruqbPnOP1pRpPeR8c2yv1XlMWPXN46yQ9o24kXnniuD9GKuHqHNn4NR5DhIxLtbxZ75zhiPXGbPPIPLUmvAno1azWX+W9afEHMdHI30oRKVlGGI71o10sFOHLDQ+gzLmurp+DrBByK9o70wGjlBBq2uPapWy1PHKG3k+f7SKgWUyySiMaLHWR6N1GnBtS1uW+QMvds6//8ZPCbAq5BrS4ibxULobmk9nfXEoiAeYVFL66Cv4KJVZLzQGtyaR2N4kjM4BvqLHjJJ2PzXvAfYtixCORviXrjqon6vjz3peLRPyjjiPSIn6mK9z7JFVXRtq25EDWjoSh3OAlUn1Jy0JhFsxfhO07+S8RJJ8+QY4d0igK42bMVek3KAWo6nZebkh1xqdQC7sozRjHNZgee/a9xXO+PNwrf1bxto80N5Ob1KnEB1bC6PRWvyXR8279qnWcI78swbH+Yjbs6j0iHl/KHAMfdrdaDJLKxL4ck4sbyTtCdjaFEHC5ZY3gHuwTY9HC7uvW1spdJECUMIWcM1IwGhpe2EMhQOq1nEoMafCWgS8W43dGMxNE046rMQxmZBU6AHO+7BP3i3w6smH8101mRxfBGdQuZMkOHvnRrIqssT+5Sp7LesGNLyP5AbywhzhKIySOLJSlFodVA7c2uM/+oBdJ/2apSiuijmAD6ZXT/kbxh3FXFywaDQ1Sh6uVY/NmYY5jmI9BSnzFpgccSDH2+1MBmiEit7Vr9JMn1bDMGxA8vbOdKThaiYSbzzGs6tPsWyzHGgo8CtN2oQHINTqW7+g2dcarkP5r170M28tGXX51mIlfG/BOLeDnjkxNMgzPTOL"],"X-Forwarded-For":["39.96.194.80"],"X-Real-Ip":["39.96.194.80"],"X-Remoteaddr":["172.21.0.1"]}`
var headerMap http.Header
if err := json.Unmarshal([]byte(hds), &headerMap); err != nil {
t.Error(fmt.Sprintf("解析 headers 失败: %v", err))
return
}
ctx := context.Background()
respHeader, respBody, err := request.Post(ctx, uri, body, request.WithHeaders(headerMap))
if err != nil {
t.Errorf("请求失败: [%s]-%v", string(respBody), err)
return
}
t.Logf("响应体:%s", string(respBody))
t.Logf("响应头:%v", respHeader)
}

View File

@ -1,42 +0,0 @@
package po
import (
"encoding/json"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"plugins/lsxd_wechat_cpn/internal/vo"
)
type Ext struct {
OrderNo string `json:"order_no"`
OrderBelong string `json:"order_belong"`
StockCreatorName string `json:"stock_creator_name"`
}
type OrderReq struct {
Label string `json:"label" validate:"required"`
StockCreatorMchId string `json:"stock_creator_mch_id" validate:"required"`
Appid string `json:"appid" validate:"required"`
OutRequestNo string `json:"out_request_no" validate:"required"`
Openid string `json:"openid" validate:"required"`
StockId string `json:"stock_id" validate:"required"`
CallBack string `json:"call_back" validate:"required"`
Ext *Ext `json:"ext"`
}
func (c *OrderReq) Json() ([]byte, error) {
b, err := json.Marshal(c)
if err != nil {
return nil, proto.ErrorParamFail("order req json Marshal error: %v", err)
}
return b, nil
}
type OrderResp struct {
Code vo.Code `json:"code"`
Message string `json:"message"`
Data struct {
CouponId string `json:"coupon_id"`
} `json:"data,omitempty"`
}

View File

@ -1,114 +0,0 @@
package po
import (
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/go-playground/validator/v10"
"plugins/lsxd_wechat_cpn/internal/vo"
)
type QueryReq struct {
Label string `json:"label" validate:"required"`
CouponId string `json:"coupon_id" validate:"required"`
Appid string `json:"app_id" validate:"required"`
Openid string `json:"open_id" validate:"required"`
IsNotifyData bool `json:"is_notify_data,omitempty"`
IsForce bool `json:"is_force,omitempty"` // true则绕过缓存直接查询微信服务器
}
func (c *QueryReq) Json() ([]byte, error) {
b, err := json.Marshal(c)
if err != nil {
return nil, proto.ErrorParamFail("query req json Marshal error: %v", err)
}
return b, nil
}
type QueryResp struct {
Code vo.Code `json:"code"`
Message string `json:"message"`
Data Data `json:"data,omitempty"`
}
// Data 根结构体,包含 query 和 notify 两个子对象
type Data struct {
Query *Query `json:"query"`
Notify *Notify `json:"notify,omitempty"`
}
// Query query 子对象结构体
type Query struct {
StockCreatorMchid string `json:"stock_creator_mchid"` // 库存创建商户号
StockId string `json:"stock_id"` // 库存ID
CouponId string `json:"coupon_id" validate:"required"` // 优惠券ID
CutToMessage CutToMessage `json:"cut_to_message"` // 立减到价信息
CouponName string `json:"coupon_name"` // 优惠券名称
Status string `json:"status"` // 状态USED/UNUSED 等)
Description string `json:"description"` // 描述
CreateTime string `json:"create_time"` // 创建时间RFC3339 格式)
CouponType string `json:"coupon_type"` // 优惠券类型NORMAL 等)
NoCash bool `json:"no_cash"` // 是否无现金
AvailableBeginTime string `json:"available_begin_time"` // 可用开始时间
AvailableEndTime string `json:"available_end_time"` // 可用结束时间
Singleitem bool `json:"singleitem"` // 是否单品优惠
NormalCouponInformation NormalCouponInfo `json:"normal_coupon_information"` // 普通优惠券信息
OutRequestNo string `json:"out_request_no"` // 外部请求号
AvailableBalance int `json:"available_balance"` // 可用余额
BusinessType string `json:"business_type"` // 业务类型
DiscountMsg *interface{} `json:"discount_msg"` // 优惠说明null 用指针兼容)
}
// CutToMessage 立减到价信息结构体
type CutToMessage struct {
SinglePriceMax float64 `json:"single_price_max"` // 单品最高价格
CutToPrice float64 `json:"cut_to_price"` // 立减到价
}
// NormalCouponInfo 普通优惠券信息结构体
type NormalCouponInfo struct {
CouponAmount int `json:"coupon_amount"` // 优惠券金额(单位:分)
TransactionMinimum int `json:"transaction_minimum"` // 最低交易金额(单位:分)
}
// Notify notify 子对象结构体
type Notify struct {
StockCreatorMchid string `json:"stock_creator_mchid"` // 库存创建商户号
StockId string `json:"stock_id"` // 库存ID
CouponId string `json:"coupon_id"` // 优惠券ID
SingleitemDiscountOff *interface{} `json:"singleitem_discount_off"` // 单品优惠减免null 用指针兼容)
DiscountTo *interface{} `json:"discount_to"` // 优惠到价null 用指针兼容)
CouponName string `json:"coupon_name"` // 优惠券名称
Status string `json:"status"` // 状态
Description string `json:"description"` // 描述
CreateTime string `json:"create_time"` // 创建时间
CouponType string `json:"coupon_type"` // 优惠券类型
NoCash bool `json:"no_cash"` // 是否无现金
AvailableBeginTime string `json:"available_begin_time"` // 可用开始时间
AvailableEndTime string `json:"available_end_time"` // 可用结束时间
Singleitem bool `json:"singleitem"` // 是否单品优惠
NormalCouponInformation NormalCouponInfo `json:"normal_coupon_information"` // 普通优惠券信息
ConsumeInformation *ConsumeInfo `json:"consume_information"` // 核销信息(可能为空,用指针)
BusinessType string `json:"business_type"` // 业务类型
}
// ConsumeInfo 核销信息结构体
type ConsumeInfo struct {
ConsumeTime string `json:"consume_time"` // 核销时间
ConsumeMchid string `json:"consume_mchid"` // 核销商户号
TransactionId string `json:"transaction_id"` // 交易ID
ConsumeAmount int `json:"consume_amount"` // 核销金额
GoodsDetail *interface{} `json:"goods_detail"` // 商品详情null 用指针兼容)
}
func (req *Data) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorParamFail(fmt.Sprintf("参数有误: %v", err))
}
}
return nil
}

View File

@ -1,254 +0,0 @@
package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/go-playground/validator/v10"
"net/http"
"plugins/lsxd_wechat_cpn/internal/po"
"plugins/lsxd_wechat_cpn/internal/vo"
"plugins/utils/request"
)
type SignConfig struct {
Key string `validate:"required" json:"key" validate:"required"`
IV string `validate:"required" json:"iv" validate:"required"`
}
type VerifyConfig struct {
Key string `validate:"required" json:"key" validate:"required"`
IV string `validate:"required" json:"iv" validate:"required"`
}
type Config struct {
BaseUrl string `validate:"required" json:"base_url"`
NotifyUrl string `validate:"required" json:"notify_url"`
SignConfig SignConfig `json:"sign_config" validate:"required"`
VerifyConfig VerifyConfig `json:"verify_config" validate:"required"`
Source string `json:"source" validate:"required"` // 来源 ymt
Label string `json:"label" validate:"required"` // 标识 发券商户
}
func (c *Config) Post(ctx context.Context, method string, reqBody []byte) ([]byte, http.Header, error) {
v, err := c.Sign(reqBody)
if err != nil {
return nil, nil, err
}
h := http.Header{
"Content-Type": []string{"application/json"},
"Source": []string{c.Source},
"Verify": []string{v},
}
respHeader, respBody, err := request.Post(ctx, c.BaseUrl+method, reqBody, request.WithHeaders(h), request.WithTimeout(5))
if err != nil {
return nil, nil, proto.ErrorRequestFail(err.Error())
}
return respBody, respHeader, nil
}
func (c *Config) Sign(reqBody []byte) (string, error) {
sign, err := Sign(reqBody, []byte(c.SignConfig.Key), []byte(c.SignConfig.IV))
if err != nil {
return "", proto.ErrorSignFail(err.Error())
}
return sign, nil
}
func (c *Config) verify(reqBody []byte, reqSign string) error {
resSign, err := Sign(reqBody, []byte(c.VerifyConfig.Key), []byte(c.VerifyConfig.IV))
if err != nil {
return proto.ErrorSignFail(err.Error())
}
if resSign != reqSign {
return proto.ErrorSignFail("验签失败")
}
return nil
}
func (c *Config) validate() error {
if err := validator.New().Struct(c); err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
}
}
return nil
}
func transConfig(config []byte) (*Config, error) {
var c Config
if err := json.Unmarshal(config, &c); err != nil {
return nil, proto.ErrorConfigFail(err.Error())
}
if err := c.validate(); err != nil {
return nil, err
}
return &c, nil
}
func (c *Config) orderReq(order *proto.OrderRequest_Order, product *proto.OrderRequest_Product) (*po.OrderReq, error) {
if order.Extra == nil {
return nil, proto.ErrorParamFail("order拓展参数,不能为空")
}
var extra struct {
AppId string `json:"app_id"`
StockCreatorMchId string `json:"stock_creator_mchid"`
StockCreatorName string `json:"stock_creator_name"`
YmtOrderNo string `json:"ymt_order_no"`
YmtActivityName string `json:"ymt_activity_name"`
}
if err := json.Unmarshal(order.Extra, &extra); err != nil {
return nil, proto.ErrorParamFail("order拓展参数 json unmarshal error: %v", err)
}
ext := &po.Ext{
OrderNo: extra.YmtOrderNo,
OrderBelong: extra.YmtActivityName,
StockCreatorName: extra.StockCreatorName,
}
return &po.OrderReq{
Label: c.Label,
StockCreatorMchId: extra.StockCreatorMchId,
Appid: extra.AppId,
OutRequestNo: order.OrderNo,
Openid: order.Account,
StockId: product.ProductNo,
CallBack: c.NotifyUrl,
Ext: ext,
}, nil
}
func orderResp(order *proto.OrderRequest_Order, bodyBytes []byte) (*proto.OrderResponse, error) {
var resp *po.OrderResp
if err := json.Unmarshal(bodyBytes, &resp); err != nil {
return nil, proto.ErrorResponseFail("order resp json unmarshal error: %v", err)
}
if !resp.Code.IsSuccess() {
return nil, proto.ErrorRequestFail("请求成功返回错误:" + resp.Message)
}
return &proto.OrderResponse{
Result: &proto.Result{
Status: proto.Status_ING,
OrderNo: order.GetOrderNo(),
TradeNo: resp.Data.CouponId,
Message: "成功",
Data: bodyBytes,
Extra: nil,
},
}, nil
}
func (c *Config) queryReq(order *proto.QueryRequest_Order) (*po.QueryReq, error) {
if order.Extra == nil {
return nil, proto.ErrorParamFail("order拓展参数,不能为空")
}
var extra struct {
AppId string `json:"app_id"`
}
if err := json.Unmarshal(order.Extra, &extra); err != nil {
return nil, proto.ErrorRequestFail("order拓展参数 json unmarshal error: %v", err)
}
if extra.AppId == "" {
return nil, proto.ErrorParamFail("appid不能为空")
}
return &po.QueryReq{
Label: c.Label,
CouponId: order.TradeNo,
Appid: extra.AppId,
Openid: order.Account,
IsNotifyData: true,
IsForce: false,
}, nil
}
func queryResp(request *proto.QueryRequest, bodyBytes []byte) (*proto.QueryResponse, error) {
var resp *po.QueryResp
if err := json.Unmarshal(bodyBytes, &resp); err != nil {
return nil, proto.ErrorResponseFail("query resp json unmarshal error: %v", err)
}
if !resp.Code.IsSuccess() {
return nil, proto.ErrorRequestFail("请求成功返回错误:" + resp.Message)
}
status := vo.Status(resp.Data.Query.Status)
return &proto.QueryResponse{
Result: &proto.Result{
Status: status.GetOrderStatus(),
OrderNo: request.Order.GetOrderNo(),
TradeNo: request.Order.TradeNo,
Message: status.GetText(),
Data: bodyBytes,
Extra: nil,
},
}, nil
}
func notifyResp(bodyBytes []byte) (*proto.NotifyResponse, error) {
var resp po.Data
if err := json.Unmarshal(bodyBytes, &resp); err != nil {
return nil, proto.ErrorParamFail("notify data json unmarshal error: %v", err)
}
if resp.Query == nil {
return nil, proto.ErrorParamFail("notify data query is nil")
}
if err := resp.Validate(); err != nil {
return nil, proto.ErrorParamFail("notify data validate error: %v", err)
}
extra := []byte(``)
status := vo.Status(resp.Query.Status).GetOrderStatus()
if status == proto.Status_WRITE_OFF {
if resp.Notify != nil && resp.Notify.ConsumeInformation != nil && resp.Notify.ConsumeInformation.ConsumeTime == "" {
var e struct {
ConsumeTime string `json:"consume_time"`
}
e.ConsumeTime = resp.Notify.ConsumeInformation.ConsumeTime
extra, _ = json.Marshal(e)
}
}
return &proto.NotifyResponse{
Result: &proto.Result{
Status: status,
OrderNo: resp.Query.OutRequestNo,
TradeNo: resp.Query.CouponId,
Message: "",
Data: bodyBytes,
Extra: extra,
},
Return: "SUCCESS",
Headers: `{"Content-Type":["application/json"]}`,
}, nil
}

View File

@ -1,52 +0,0 @@
package internal
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
)
// PKCS7Padding 对数据进行PKCS7填充
func PKCS7Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
t := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, t...)
}
// AES256CBCEncrypt 使用AES-256-CBC算法加密数据
func AES256CBCEncrypt(plaintext, key, iv []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 对明文进行PKCS7填充
padded := PKCS7Padding(plaintext, aes.BlockSize)
// 创建CBC模式的加密器
mode := cipher.NewCBCEncrypter(block, iv)
// 执行加密
ciphertext := make([]byte, len(padded))
mode.CryptBlocks(ciphertext, padded)
return ciphertext, nil
}
// Sign .
func Sign(plaintext, key, iv []byte) (string, error) {
ciphertext, err := AES256CBCEncrypt(plaintext, key, iv)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(ciphertext), nil
}

View File

@ -1,16 +0,0 @@
package vo
type Code string
const (
CodeSuccess Code = "0"
CodeFail Code = "1"
)
func (c Code) Value() string {
return string(c)
}
func (c Code) IsSuccess() bool {
return c == CodeSuccess
}

View File

@ -1,52 +0,0 @@
package vo
import "gitea.cdlsxd.cn/sdk/plugin/proto"
type Status string
const (
StatusAvailable = "SENDED"
StatusUsed = "USED" // 代金券已被用户使用(实扣);
StatusExpired = "EXPIRED" // 代金券因活动过期而失效;
// StatusRevoked 当代金券被商户主动撤销(作废)时,会触发“已失效”状态,用户无法再使用。此情况下,代金券状态不可逆,
// 需重新发放才能再次使用。建议通过查询接口确认代金券状态,并确保在有效期内合理管理券的发放与使用。
StatusRevoked = "REVOKED" // 代金券被商户主动撤销。
StatusRecover = "RECOVER" // 代金券被系统回收,可能因未使用或规则触发;
)
var statusTextMap = map[Status]string{
StatusAvailable: "可用",
StatusUsed: "已实扣(代金券已被用户使用)",
StatusExpired: "已过期(代金券因活动过期而失效)",
StatusRevoked: "已失效(代金券被商户主动撤销)",
StatusRecover: "已回收(代金券被系统回收,可能因未使用或规则触发)",
}
var queryStatusMap = map[Status]proto.Status{
StatusAvailable: proto.Status_SUCCESS,
StatusUsed: proto.Status_WRITE_OFF,
StatusExpired: proto.Status_OVERDUE,
StatusRevoked: proto.Status_REVOKE,
StatusRecover: proto.Status_INVALID,
}
func (o Status) GetText() string {
msg, ok := statusTextMap[o]
if !ok {
return ""
}
return msg
}
func (o Status) GetOrderStatus() proto.Status {
if o == "" {
return proto.Status_INVALID
}
if resultStatus, ok := queryStatusMap[o]; ok {
return resultStatus
}
return proto.Status_FAIL
}

View File

@ -1,15 +0,0 @@
package main
import (
"gitea.cdlsxd.cn/sdk/plugin/shared"
"github.com/hashicorp/go-plugin"
"plugins/lsxd_wechat_cpn/internal"
)
func main() {
plugin.Serve(&plugin.ServeConfig{
HandshakeConfig: shared.HandshakeConfig(internal.Version, internal.CookieKey, internal.CookieValue),
Plugins: shared.PluginSet(shared.NewPlugin(&internal.LSXDWeChatCpnService{}, internal.Tag)),
GRPCServer: plugin.DefaultGRPCServer,
})
}

View File

@ -1,39 +0,0 @@
module plugins/qixing_alipay_redpack
go 1.22.2
replace plugins/utils => ../../utils
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.27
github.com/go-playground/validator/v10 v10.22.0
github.com/hashicorp/go-plugin v1.6.1
github.com/stretchr/testify v1.10.0
plugins/utils v0.0.0-00010101000000-000000000000
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/hashicorp/go-hclog v0.14.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/grpc v1.64.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -1,80 +0,0 @@
gitea.cdlsxd.cn/sdk/plugin v1.0.27 h1:ocp+3n8qLlPm+yI0VpMXFWfC8gsdDrKxEowuD58T6CE=
gitea.cdlsxd.cn/sdk/plugin v1.0.27/go.mod h1:4fLMp/xB9GEBa3nJi62kXpHh7wnb9Lwjf0I8Vjaasx0=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-kratos/kratos/v2 v2.8.2 h1:EsEA7AmPQ2YQQ0FZrDWO2HgBNqeWM8z/mWKzS5UkQaQ=
github.com/go-kratos/kratos/v2 v2.8.2/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI=
github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,10 +0,0 @@
package po
import "plugins/qixing_alipay_redpack/internal/vo"
type Notify struct {
BatchId string `json:"batchId"`
BizNo string `json:"bizNo"`
Type vo.Type `json:"type"`
FailMsg string `json:"failMsg"`
}

View File

@ -1,75 +0,0 @@
package po
import (
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"plugins/qixing_alipay_redpack/internal/vo"
)
type OrderReq struct {
Amount float32 `validate:"required" json:"amount"` // 微信:0.3-200;支付宝:<400单位:元
BatchId string `validate:"required" json:"batchId"` // 批次ID
BizNo string `validate:"required" json:"bizNo"` // 业务单号,需唯一
Name string `json:"name"` // 真实姓名 支付宝必填
Phone string `json:"phone"` // 支付宝必填
//WxAppId string `json:"wxAppId"` // 公众号ID 微信必填
//OpenId string `json:"openId"` // 微信openId 微信必填
//Wishing string `json:"wishing"` // 红包祝福语,微信使用
//RedPacketName string `son:"redPacketName"` // 红包名称 默认"现金红包"
//Expand string `json:"expand"` // 拓展信息
//Remark string `json:"remark"` // 红包备注 默认“现金红包”
//SendName string `json:"sendName"` // 发送名称 微信使用默认与redPacketName相同
NotifyUrl string `validate:"required" json:"notifyUrl"`
Sign string `json:"sign"` // MD5签名
}
type OrderWechatReq struct {
Amount float32 `validate:"required" json:"amount"` // 微信:0.3-200;支付宝:<400单位:元
BatchId string `validate:"required" json:"batchId"` // 批次ID
BizNo string `validate:"required" json:"bizNo"` // 业务单号,需唯一
WxAppId string `validate:"required" json:"wxAppId"` // 公众号ID 微信必填
OpenId string `validate:"required" json:"openId"` // 微信openId 微信必填
Wishing string `validate:"required" json:"wishing"` // 红包祝福语,微信使用
RedPacketName string `validate:"required" json:"redPacketName"` // 红包名称 默认"现金红包"
Expand string `json:"expand"` // 拓展信息
Remark string `json:"remark"` // 红包备注 默认“现金红包”
SendName string `validate:"required" json:"sendName"` // 发送名称 微信使用默认与redPacketName相同
Sign string `json:"sign"` // MD5签名
}
type OrderResp struct {
Code any `json:"code"`
Msg string `json:"msg"`
}
func (o *OrderResp) IsSuccess() bool {
strCode := fmt.Sprintf("%v", o.Code)
return strCode == "000000"
}
func (o *OrderResp) GetMsg() string {
if o.IsSuccess() {
return o.Msg
}
return fmt.Sprintf("code:[%v],msg:[%s]", o.Code, o.Msg)
}
func (o *OrderResp) IsInsufficientBudget() bool {
code := vo.Code(fmt.Sprintf("%v", o.Code))
return code.IsInsufficientBudget()
}
func (o *OrderResp) GetOrderStatus() proto.Status {
if o.IsSuccess() {
return proto.Status_ING
}
return proto.Status_FAIL
}

View File

@ -1,60 +0,0 @@
package po
import (
"encoding/json"
"fmt"
"github.com/go-playground/validator/v10"
)
type Req interface {
Validate() error
ToJson() ([]byte, error)
SetSign(sign string)
}
var _ Req = (*OrderReq)(nil)
var _ Req = (*QueryReq)(nil)
func (req *OrderReq) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
func (req *OrderReq) ToJson() ([]byte, error) {
b, err := json.Marshal(req)
if err != nil {
return nil, err
}
return b, nil
}
func (req *OrderReq) SetSign(sign string) {
req.Sign = sign
}
func (req *QueryReq) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
func (req *QueryReq) ToJson() ([]byte, error) {
b, err := json.Marshal(req)
if err != nil {
return nil, err
}
return b, nil
}
func (req *QueryReq) SetSign(sign string) {
}

View File

@ -1,53 +0,0 @@
package po
import (
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"plugins/qixing_alipay_redpack/internal/vo"
)
type QueryReq struct {
BizNo string `json:"bizNo" validate:"required"`
BatchId string `json:"batchId" validate:"required"`
}
type Data struct {
BatchId string `json:"batchId"`
WxAppId string `json:"wxAppId"`
OpenId string `json:"openId"`
Name string `json:"name"`
Phone string `json:"phone"`
Amount float32 `json:"amount"`
BizNo string `json:"bizNo"`
Status string `json:"status"`
FailMsg string `json:"failMsg"`
Expand string `json:"expand"`
MchId string `json:"mchId"`
SendName string `json:"sendName"`
Remark string `json:"remark"`
}
type QueryResp struct {
Code any `json:"code"`
Msg string `json:"msg"`
Data *Data `json:"data"`
}
func (o *QueryResp) IsSuccess() bool {
strCode := fmt.Sprintf("%v", o.Code)
return strCode == "000000"
}
func (o *QueryResp) GetMsg() string {
code := vo.Code(fmt.Sprintf("%v", o.Code))
t := code.GetText()
if t == "" {
t = o.Msg
}
return fmt.Sprintf("[%s]-[%s]", code, t)
}
func (o *QueryResp) GetOrderStatus() proto.Status {
return vo.Status(o.Data.Status).GetOrderStatus()
}

View File

@ -1,135 +0,0 @@
package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"plugins/qixing_alipay_redpack/internal/po"
)
// 插件通信信息,若不对应则会报错panic
const (
Tag = "qixing_alipay_redpack"
Version = 1
CookieKey = "qixing_alipay_redpack"
CookieValue = "qixing_alipay_redpack"
)
const (
orderMethod = "/send"
queryMethod = "/received"
)
type RedPackService struct{}
func (s *RedPackService) Order(ctx context.Context, request *proto.OrderRequest) (resp2 *proto.OrderResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = fmt.Errorf("panic: %v", err)
}
}()
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
poReq, err := c.orderReq(request.Order, request.Product)
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
}
reqBody, err := req(c, poReq)
if err != nil {
return nil, err
}
url := c.BasUrl + orderMethod
bodyBytes, _, err := Post(ctx, url, reqBody)
if err != nil {
return nil, err
}
var response *po.OrderResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
}
return orderResp(request, response, bodyBytes)
}
func (s *RedPackService) Query(ctx context.Context, request *proto.QueryRequest) (resp2 *proto.QueryResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = fmt.Errorf("panic: %v", err)
}
}()
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
reqBody, err := getReq(c.queryReq(request.Order))
if err != nil {
return nil, err
}
url := c.BasUrl + queryMethod
bodyBytes, _, err := Get(ctx, url, reqBody)
if err != nil {
return nil, err
}
var response *po.QueryResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
}
if !response.IsSuccess() {
return nil, proto.ErrorRequestFail(response.GetMsg())
}
return queryResp(request, response, bodyBytes), nil
}
func (s *RedPackService) Balance(ctx context.Context, request *proto.QueryRequest) (resp []byte, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = fmt.Errorf("panic: %v", err)
}
}()
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
url := c.BasUrl + "/config?batchId=" + c.BatchId
bodyBytes, _, err := Get(ctx, url, nil)
if err != nil {
return nil, err
}
return bodyBytes, nil
}
func (s *RedPackService) Notify(_ context.Context, request *proto.NotifyRequest) (resp2 *proto.NotifyResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = fmt.Errorf("panic: %v", err)
}
}()
n, err := notifyReq(request)
if err != nil {
return nil, err
}
return notifyResp(n, request.Body)
}

View File

@ -1,126 +0,0 @@
package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/stretchr/testify/assert"
"testing"
)
var server = &RedPackService{}
// config
// https://ceshi.myviding.com/cdc-api/api/external/red-packet 测试
// https://cdc.myviding.com/cdc-api/api/external/red-packet 生产
func config() []byte {
c := &Config{
NchName: "启星支付宝红包",
BasUrl: "https://cdc.myviding.com/cdc-api/api/external/red-packet",
AppKey: "ZU2Y2HHQK00N5TP4",
BatchId: "20964224",
NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/supplier/notify/52",
}
marshal, _ := json.Marshal(c)
return marshal
}
func TestConfig(t *testing.T) {
t.Run("TestConfig", func(t *testing.T) {
c := config()
fmt.Printf("%s\n", string(c))
assert.NotEmpty(t, c)
})
}
func Test_OrderAlipay(t *testing.T) {
request := &proto.OrderRequest{
Config: config(),
Order: &proto.OrderRequest_Order{
OrderNo: "lsxd20250929001",
Account: "18666173766",
Quantity: 1,
Amount: 0.01,
Extra: []byte(`{"name":"李子铭"}`),
},
Product: &proto.OrderRequest_Product{
ProductNo: "",
Price: 0.01,
Extra: []byte(`{"wishing":"支付宝测试红包"}`),
},
}
t.Run("Test_OrderAlipay", func(t *testing.T) {
got, err := server.Order(context.Background(), request)
if err != nil {
t.Errorf("Order() error = %v", err)
return
}
fmt.Printf("%s", got.String())
assert.Equal(t, int(proto.Status_ING), int(got.Result.Status))
})
}
func Test_Query(t *testing.T) {
request := &proto.QueryRequest{
Config: config(),
Order: &proto.QueryRequest_Order{
OrderNo: "lsxd202306071545141532",
TradeNo: "",
Account: "",
Extra: []byte(``),
},
}
t.Run("Test_Query", func(t *testing.T) {
got, err := server.Query(context.Background(), request)
if err != nil {
t.Errorf("Query() error = %v", err)
return
}
fmt.Printf("%+v \n", got)
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})
}
func Test_Balance(t *testing.T) {
request := &proto.QueryRequest{
Config: config(),
Order: &proto.QueryRequest_Order{
OrderNo: "lsxd202306071545141532",
TradeNo: "",
Account: "",
Extra: []byte(`{"product_no":"20976200"}`),
},
}
t.Run("Test_Balance", func(t *testing.T) {
got, err := server.Balance(context.Background(), request)
if err != nil {
t.Errorf("Query() error = %v", err)
return
}
fmt.Printf("%s \n", string(got))
})
}
func Test_Notify(t *testing.T) {
in := &proto.NotifyRequest{
Config: config(),
Queries: []byte(``),
Headers: []byte(``),
Body: []byte(`{
"batchId": "20300480",
"bizNo": "订单123456",
"type": "RECEIVED",
"failMsg": "失败原因"
}`),
}
t.Run("TestNotify", func(t *testing.T) {
got, err := server.Notify(context.Background(), in)
if err != nil {
t.Errorf("Notify() error = %v", err)
return
}
fmt.Printf("TestNotify : %+v \n", got)
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})
}

View File

@ -1,155 +0,0 @@
package internal
import (
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/go-playground/validator/v10"
"net/http"
"plugins/qixing_alipay_redpack/internal/po"
"plugins/utils/helper"
)
type Config struct {
NchName string `validate:"required" json:"mch_name"` // 商户名称
BasUrl string `validate:"required" json:"base_url"` // 请求地址
AppKey string `validate:"required" json:"app_key"` // appKey密钥
BatchId string `validate:"required" json:"batch_id"` // 批次账号
NotifyUrl string `validate:"required" json:"notify_url"` // 回调地址
}
func (c *Config) validate() error {
err := validator.New().Struct(c)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
}
}
return nil
}
func transConfig(config []byte) (*Config, error) {
var c Config
if err := json.Unmarshal(config, &c); err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("配置参数解析失败: %v", err))
}
if err := c.validate(); err != nil {
return nil, err
}
return &c, nil
}
func (c *Config) orderReq(order *proto.OrderRequest_Order, product *proto.OrderRequest_Product) (*po.OrderReq, error) {
//var productExtra struct {
// Wishing string `json:"wishing"`
//}
//if err := json.Unmarshal(product.Extra, &productExtra); err != nil {
// return nil, proto.ErrorParamFail(fmt.Sprintf("启星product拓展参数解析失败: %v", err))
//}
if !helper.IsPhoneNumber(order.Account) && !helper.IsEmail(order.Account) {
return nil, proto.ErrorParamFail("启星支付宝红包只支持账号领取")
}
var orderExtra struct {
Name string `json:"name"`
}
if err := json.Unmarshal(order.Extra, &orderExtra); err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("支付宝红包order拓展参数解析失败: %v", err))
}
return &po.OrderReq{
Amount: order.Amount,
BatchId: c.BatchId,
BizNo: order.OrderNo,
Name: orderExtra.Name,
Phone: order.Account,
NotifyUrl: c.NotifyUrl,
Sign: "",
}, nil
}
func orderResp(request *proto.OrderRequest, resp *po.OrderResp, bodyBytes []byte) (*proto.OrderResponse, error) {
//if !resp.IsSuccess() {
// if resp.IsInsufficientBudget() {
// //return nil, proto.ErrorInsufficientBudget(resp.GetMsg())
// }
//}
return &proto.OrderResponse{
Result: &proto.Result{
Status: resp.GetOrderStatus(),
OrderNo: request.Order.OrderNo,
TradeNo: "",
Message: resp.GetMsg(),
Data: bodyBytes,
},
}, nil
}
func (c *Config) queryReq(in *proto.QueryRequest_Order) *po.QueryReq {
return &po.QueryReq{
BizNo: in.OrderNo,
BatchId: c.BatchId,
}
}
func queryResp(request *proto.QueryRequest, resp *po.QueryResp, bodyBytes []byte) *proto.QueryResponse {
return &proto.QueryResponse{
Result: &proto.Result{
OrderNo: request.Order.OrderNo,
TradeNo: "",
Status: resp.GetOrderStatus(),
Message: resp.Msg,
Data: bodyBytes,
},
}
}
func notifyReq(in *proto.NotifyRequest) (*po.Notify, error) {
var n *po.Notify
if err := json.Unmarshal(in.Body, &n); err != nil {
return nil, proto.ErrorParamFail(fmt.Sprintf("notify拓展参数解析失败: %v", err))
}
return n, nil
}
func notifyResp(n *po.Notify, data []byte) (*proto.NotifyResponse, error) {
pb := &proto.NotifyResponse{
Result: &proto.Result{
Status: n.Type.GetOrderType(),
OrderNo: n.BizNo,
TradeNo: "",
Message: n.FailMsg,
Data: data,
Extra: nil,
},
Return: `{"code":"success"}`,
}
headers := make(http.Header)
headers.Set("Content-Type", "application/json")
headersBytes, err := json.Marshal(headers)
if err != nil {
return nil, err
}
pb.Headers = string(headersBytes)
return pb, nil
}

View File

@ -1,119 +0,0 @@
package internal
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"gitea.cdlsxd.cn/sdk/plugin/utils"
"net/http"
"net/url"
"plugins/qixing_alipay_redpack/internal/po"
"plugins/utils/request"
"strings"
)
func req(config *Config, req po.Req) ([]byte, error) {
if err := req.Validate(); err != nil {
return nil, err
}
var strToBeSigned strings.Builder
kvRows := utils.SortStructJsonTag(req)
for _, kv := range kvRows {
if kv.Key == "sign" || kv.Value == "" {
continue
}
strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
}
s := strToBeSigned.String() + config.AppKey
req.SetSign(MD5(s))
return req.ToJson()
}
func getReq(req po.Req) (url.Values, error) {
if err := req.Validate(); err != nil {
return nil, err
}
//var strToBeSigned strings.Builder
uv := url.Values{}
kvRows := utils.SortStructJsonTag(req)
for _, kv := range kvRows {
if kv.Key == "sign" || kv.Value == "" {
continue
}
uv.Set(kv.Key, kv.Value)
//strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
}
//s := strToBeSigned.String() + config.AppKey
//uv.Set("sign", MD5(s))
return uv, nil
}
func Post(ctx context.Context, url string, reqBody []byte) ([]byte, http.Header, error) {
//fmt.Printf("url: %s\n", url)
//fmt.Printf("reqBody: %s\n", reqBody)
h := http.Header{
"Content-Type": []string{"application/json"},
}
respHeader, respBody, err := request.Post(ctx, url, reqBody, request.WithHeaders(h))
if err != nil {
return nil, nil, proto.ErrorRequestFail(err.Error())
}
return respBody, respHeader, nil
}
func Get(ctx context.Context, url string, uv url.Values) ([]byte, http.Header, error) {
requestUrl := url
if uv != nil {
requestUrl = url + "?" + uv.Encode()
}
h := http.Header{
"Content-Type": []string{"application/x-www-form-urlencoded"},
}
respHeader, respBody, err := request.Get(ctx, requestUrl, request.WithHeaders(h))
if err != nil {
return nil, nil, proto.ErrorRequestFail(err.Error())
}
return respBody, respHeader, nil
}
func MD5(data string) string {
// 创建一个 MD5 哈希对象
hash := md5.New()
// 写入待加密的数据
hash.Write([]byte(data))
// 获取 MD5 哈希值
hashBytes := hash.Sum(nil)
// 将 MD5 哈希值转换为16进制字符串
return hex.EncodeToString(hashBytes)
}
func Verify(n *po.Notify, appKey string) (bool, error) {
var strToBeSigned strings.Builder
uv := url.Values{}
kvRows := utils.SortStructJsonTag(n)
for _, kv := range kvRows {
if kv.Key == "sign" || kv.Value == "" {
continue
}
uv.Set(kv.Key, kv.Value)
strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
}
return false, nil
}

View File

@ -1,27 +0,0 @@
package vo
type Code string
var CodeTextMap = map[Code]string{
"000000": "成功",
"100000": "内部错误(联系技术支持)",
"100001": "验签错误(检查签名算法)",
"100010": "活动批次不存在(检查batchId)",
"100011": "活动预算不足(联系客服增加预算)",
"100012": "业务编号已存在(更换bizNo)",
"100014": "缺少请求参数(检查必填字段)",
"100015": "发放错误(检查金额/账户信息)",
"100088": "接口请求频繁(重新调用接口)",
}
func (o Code) GetText() string {
msg, ok := CodeTextMap[o]
if !ok {
return ""
}
return msg
}
func (o Code) IsInsufficientBudget() bool {
return o == "100011"
}

View File

@ -1,44 +0,0 @@
package vo
import "gitea.cdlsxd.cn/sdk/plugin/proto"
type Status string
const (
StatusUnclaimed = "0"
StatusReceived = "1"
StatusExpired = "2"
StatusFailed = "3"
)
var StatusTextMap = map[Status]string{
StatusUnclaimed: "未领取",
StatusReceived: "已领取",
StatusExpired: "已过期",
StatusFailed: "发放失败",
}
var StatusMap = map[Status]proto.Status{
StatusUnclaimed: proto.Status_ING,
StatusReceived: proto.Status_SUCCESS,
StatusExpired: proto.Status_FAIL,
StatusFailed: proto.Status_FAIL,
}
func (o Status) GetText() string {
msg, ok := StatusTextMap[o]
if !ok {
return ""
}
return msg
}
func (o Status) GetOrderStatus() proto.Status {
if o == "" {
return proto.Status_INVALID
}
if resultStatus, ok := StatusMap[o]; ok {
return resultStatus
}
return proto.Status_INVALID
}

View File

@ -1,41 +0,0 @@
package vo
import "gitea.cdlsxd.cn/sdk/plugin/proto"
type Type string
const (
TypeReceived = "RECEIVED"
TypeRefund = "REFUND"
TypeFailed = "FAILED"
)
var TypeTextMap = map[Type]string{
TypeReceived: "领取成功",
TypeRefund: "已过期退回",
TypeFailed: "领取失败",
}
var TypeMap = map[Type]proto.Status{
TypeReceived: proto.Status_SUCCESS,
TypeRefund: proto.Status_FAIL,
TypeFailed: proto.Status_FAIL,
}
func (o Type) GetText() string {
msg, ok := TypeTextMap[o]
if !ok {
return ""
}
return msg
}
func (o Type) GetOrderType() proto.Status {
if o == "" {
return proto.Status_INVALID
}
if resultType, ok := TypeMap[o]; ok {
return resultType
}
return proto.Status_INVALID
}

View File

@ -1,15 +0,0 @@
package main
import (
"gitea.cdlsxd.cn/sdk/plugin/shared"
"github.com/hashicorp/go-plugin"
"plugins/qixing_alipay_redpack/internal"
)
func main() {
plugin.Serve(&plugin.ServeConfig{
HandshakeConfig: shared.HandshakeConfig(internal.Version, internal.CookieKey, internal.CookieValue),
Plugins: shared.PluginSet(shared.NewPlugin(&internal.RedPackService{}, internal.Tag)),
GRPCServer: plugin.DefaultGRPCServer,
})
}

View File

@ -1,39 +0,0 @@
module plugins/qixing_wechat_redpack
go 1.22.2
replace plugins/utils => ../../utils
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.26
github.com/go-playground/validator/v10 v10.22.0
github.com/hashicorp/go-plugin v1.6.1
github.com/stretchr/testify v1.10.0
plugins/utils v0.0.0-00010101000000-000000000000
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/hashicorp/go-hclog v0.14.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/grpc v1.64.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -1,80 +0,0 @@
gitea.cdlsxd.cn/sdk/plugin v1.0.26 h1:n0HZNqftz7egsZp9QaNC0bhQTHXjr+71q7lNdUCQemc=
gitea.cdlsxd.cn/sdk/plugin v1.0.26/go.mod h1:4fLMp/xB9GEBa3nJi62kXpHh7wnb9Lwjf0I8Vjaasx0=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-kratos/kratos/v2 v2.8.2 h1:EsEA7AmPQ2YQQ0FZrDWO2HgBNqeWM8z/mWKzS5UkQaQ=
github.com/go-kratos/kratos/v2 v2.8.2/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI=
github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,25 +0,0 @@
package po
import (
"fmt"
"github.com/go-playground/validator/v10"
"plugins/qixing_wechat_redpack/internal/vo"
)
type Notify struct {
BatchId string `json:"batchId" validate:"required"`
BizNo string `json:"bizNo" validate:"required"`
Type vo.Type `json:"type" validate:"required"`
Sign string `json:"sign" validate:"required"`
FailMsg string `json:"failMsg"`
}
func (req *Notify) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}

View File

@ -1,56 +0,0 @@
package po
import (
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
)
type OrderReq struct {
Amount float32 `validate:"required" json:"amount"` // 微信:0.3-200;支付宝:<400单位:元
BatchId string `validate:"required" json:"batchId"` // 批次ID
BizNo string `validate:"required" json:"bizNo"` // 业务单号,需唯一
//Name string `json:"name"` // 支付宝必填
//Phone string `json:"phone"` // 支付宝必填
WxAppId string `validate:"required" json:"wxAppId"` // 公众号ID 微信必填
OpenId string `validate:"required" json:"openId"` // 微信openId 微信必填
Wishing string `validate:"required" json:"wishing"` // 红包祝福语,微信使用
RedPacketName string `json:"redPacketName"` // 红包名称 默认"现金红包"
Remark string `json:"remark"` // 红包备注 默认“现金红包”-- 微信红包微信后台能导出每日的对账单里面有remark不给用户展示
SendName string `json:"sendName"` // 发送名称 微信使用默认与redPacketName相同
NotifyUrl string `validate:"required" json:"notifyUrl"`
//Expand string `json:"expand"` // 拓展信息
Sign string `json:"sign"` // MD5签名
}
type OrderResp struct {
Code any `json:"code"`
Msg string `json:"msg"`
}
func (o *OrderResp) IsSuccess() bool {
return fmt.Sprintf("%v", o.Code) == "000000"
}
func (o *OrderResp) GetMsg() string {
if o.IsSuccess() {
return o.Msg
}
if o.Msg == "" {
return fmt.Sprintf("code:[%v],msg:[%s]", o.Code, "上游返回错误")
}
return o.Msg
}
func (o *OrderResp) GetOrderStatus() proto.Status {
if o.IsSuccess() {
return proto.Status_ING
}
return proto.Status_FAIL
}

View File

@ -1,60 +0,0 @@
package po
import (
"encoding/json"
"fmt"
"github.com/go-playground/validator/v10"
)
type Req interface {
Validate() error
ToJson() ([]byte, error)
SetSign(sign string)
}
var _ Req = (*OrderReq)(nil)
var _ Req = (*QueryReq)(nil)
func (req *OrderReq) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
func (req *OrderReq) ToJson() ([]byte, error) {
b, err := json.Marshal(req)
if err != nil {
return nil, err
}
return b, nil
}
func (req *OrderReq) SetSign(sign string) {
req.Sign = sign
}
func (req *QueryReq) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
func (req *QueryReq) ToJson() ([]byte, error) {
b, err := json.Marshal(req)
if err != nil {
return nil, err
}
return b, nil
}
func (req *QueryReq) SetSign(sign string) {
}

View File

@ -1,48 +0,0 @@
package po
import (
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"plugins/qixing_wechat_redpack/internal/vo"
)
type QueryReq struct {
BizNo string `json:"bizNo" validate:"required"`
BatchId string `json:"batchId" validate:"required"`
}
type Data struct {
BatchId string `json:"batchId"`
WxAppId string `json:"wxAppId"`
OpenId string `json:"openId"`
Name string `json:"name"`
Phone string `json:"phone"`
Amount float32 `json:"amount"`
BizNo string `json:"bizNo"`
Status string `json:"status"`
FailMsg string `json:"failMsg"`
Expand string `json:"expand"`
MchId string `json:"mchId"`
SendName string `json:"sendName"`
Remark string `json:"remark"`
}
type QueryResp struct {
Code any `json:"code"`
Msg string `json:"msg"`
Data *Data `json:"data"`
}
func (o *QueryResp) IsSuccess() bool {
strCode := fmt.Sprintf("%v", o.Code)
return strCode == "000000"
}
func (o *QueryResp) GetMsg() string {
code := vo.Code(fmt.Sprintf("%v", o.Code))
return fmt.Sprintf("code[%s],msg[%s],text[%s]", code, o.Msg, code.GetText())
}
func (o *QueryResp) GetOrderStatus() proto.Status {
return vo.Status(o.Data.Status).GetOrderStatus()
}

View File

@ -1,149 +0,0 @@
package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"plugins/qixing_wechat_redpack/internal/po"
)
// 插件通信信息,若不对应则会报错panic
const (
Tag = "qixing_wechat_redpack"
Version = 1
CookieKey = "qixing_wechat_redpack"
CookieValue = "qixing_wechat_redpack"
)
const (
orderMethod = "/send"
queryMethod = "/received"
)
// QiXingWechatRedPackService 启星,不支持幂等
type QiXingWechatRedPackService struct{}
func (s *QiXingWechatRedPackService) Order(ctx context.Context, request *proto.OrderRequest) (resp2 *proto.OrderResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
poReq, err := c.orderReq(request.Order, request.Product)
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
}
reqBody, err := req(c, poReq)
if err != nil {
return nil, err
}
url := c.BasUrl + orderMethod
bodyBytes, _, err := Post(ctx, url, reqBody)
if err != nil {
return nil, err
}
var response *po.OrderResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
}
return orderResp(request, response, bodyBytes), nil
}
func (s *QiXingWechatRedPackService) Query(ctx context.Context, request *proto.QueryRequest) (resp2 *proto.QueryResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
reqBody, err := getReq(c.queryReq(request.Order))
if err != nil {
return nil, err
}
url := c.BasUrl + queryMethod
bodyBytes, _, err := Get(ctx, url, reqBody)
if err != nil {
return nil, err
}
var response *po.QueryResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
}
if !response.IsSuccess() {
return nil, proto.ErrorRequestFail(response.GetMsg())
}
return queryResp(request, response, bodyBytes), nil
}
func (s *QiXingWechatRedPackService) Balance(ctx context.Context, request *proto.QueryRequest) (resp []byte, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
url := c.BasUrl + "/config?batchId=" + c.BatchId
bodyBytes, _, err := Get(ctx, url, nil)
if err != nil {
return nil, err
}
return bodyBytes, nil
}
func (s *QiXingWechatRedPackService) Notify(_ context.Context, request *proto.NotifyRequest) (resp2 *proto.NotifyResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
if request.Body == nil {
return nil, proto.ErrorParamFail("body is nil")
}
n, err := notifyReq(request)
if err != nil {
return nil, err
}
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
if !Verify(n, c.AppKey) {
return nil, proto.ErrorSignFail("签名验证失败")
}
return notifyResp(n, request.Body)
}

View File

@ -1,138 +0,0 @@
package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/stretchr/testify/assert"
"testing"
)
var server = &QiXingWechatRedPackService{}
// config
// https://ceshi.myviding.com/cdc-api/api/external/red-packet 测试
// https://cdc.myviding.com/cdc-api/api/external/red-packet 生产
func config() []byte {
c := &Config{
Mode: "wechat_redpack_mode",
NchName: "启星微信红包",
AppKey: "ZU2Y2HHQK00N5TP4",
BatchId: "20062389", // 对应启星的微信商户号1730083226
BasUrl: "https://cdc.myviding.com/cdc-api/api/external/red-packet",
//NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/supplier/notify/51", // 测试环境
NotifyUrl: "https://market.86698.cn/v1/supplier/notify/51", // 生产环境
Official: Official{
Name: "福建兴旺网络科技有限公司",
AppId: "wxe3bd59243545fa8a",
AppSecret: "4c9649cb998f71038e187b4c58f5fda0",
OauthKey: "bcee0c6753b2a31c792a91fe9f9f1666",
OauthUrl: "https://utils.85938.cn/utils/v1/wechat/oauth/fjxw",
},
MiniProgram: MiniProgram{
Name: "兴旺专属fl官",
AppId: "wx075c784fe71d4d04",
AppSecret: "5612971815ad7bf731bfec6b993b4128",
EnvVersion: "release", // envVersion 正式版为"release",体验版为"trial",开发版为"develop",仅在微信外打开时生效。
},
}
marshal, _ := json.Marshal(c)
return marshal
}
func Test_Config(t *testing.T) {
t.Run("TestConfig", func(t *testing.T) {
c := config()
fmt.Printf("%s\n", string(c))
assert.NotEmpty(t, c)
})
}
func Test_Order(t *testing.T) {
request := &proto.OrderRequest{
Config: config(),
Order: &proto.OrderRequest_Order{
OrderNo: "lsxd20250929005",
Account: "ojbqr6HpeWKFy9Sgdx8yCmmeVJiw",
Quantity: 1,
Amount: 0.3,
Extra: []byte(``),
},
Product: &proto.OrderRequest_Product{
ProductNo: "",
Price: 0.3,
Type: 0,
Extra: []byte(`{"batch_name":"batch name恭喜发财红包拿来红包祝福语", "batch_remark":"batch remark 恭喜发财,红包拿来,红包备注", "goods_name":"红包名称xx红包"}`),
},
}
t.Run("Test_Order", func(t *testing.T) {
got, err := server.Order(context.Background(), request)
if err != nil {
t.Errorf("Order() error = %v", err)
return
}
fmt.Printf("响应报文=%s\n", got.Result.Data)
fmt.Printf("%s", got.String())
assert.Equal(t, int(proto.Status_ING), int(got.Result.Status))
})
}
func Test_Query(t *testing.T) {
request := &proto.QueryRequest{
Config: config(),
Order: &proto.QueryRequest_Order{
OrderNo: "202512251641128290010039",
TradeNo: "",
Account: "",
Extra: []byte(``),
},
}
t.Run("Test_Query", func(t *testing.T) {
got, err := server.Query(context.Background(), request)
if err != nil {
t.Errorf("Query() error = %v", err)
return
}
fmt.Printf("%+v \n", got)
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})
}
func Test_Balance(t *testing.T) {
request := &proto.QueryRequest{
Config: config(),
Order: &proto.QueryRequest_Order{
OrderNo: "lsxd202306071545141532",
TradeNo: "",
Account: "",
Extra: []byte(``),
},
}
t.Run("Test_Balance", func(t *testing.T) {
got, err := server.Balance(context.Background(), request)
if err != nil {
t.Errorf("Query() error = %v", err)
return
}
fmt.Printf("%s \n", string(got))
})
}
func TestNotify(t *testing.T) {
in := &proto.NotifyRequest{
Config: config(),
Queries: []byte(``),
Headers: []byte(``),
Body: []byte(`{"bizNo":"lsxd20250929003","sign":"e71e685d8d9544aad0f32e699fd98e06","batchId":"20062389","type":"SENT"}`),
}
t.Run("TestNotify", func(t *testing.T) {
got, err := server.Notify(context.Background(), in)
if err != nil {
t.Errorf("Notify() error = %v", err)
return
}
fmt.Printf("TestNotify : %+v \n", got)
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})
}

View File

@ -1,176 +0,0 @@
package internal
import (
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/go-playground/validator/v10"
"net/http"
"plugins/qixing_wechat_redpack/internal/po"
"plugins/qixing_wechat_redpack/internal/vo"
)
type Official struct {
Name string `json:"name"` // 公众号名称
AppId string `validate:"required" json:"app_id"`
AppSecret string `json:"app_secret"`
OauthKey string `json:"oauth_key"`
OauthUrl string `json:"oauth_url"`
}
type MiniProgram struct {
Name string `json:"name"`
AppId string `json:"app_id"`
AppSecret string `json:"app_secret"`
EnvVersion string `json:"env_version"`
}
type Config struct {
Mode vo.Mode `validate:"required" json:"mode"` // 商户名称
NchName string `validate:"required" json:"mch_name"` // 商户名称
AppKey string `validate:"required" json:"app_key"` // appKey密钥
BatchId string `validate:"required" json:"batch_id"` // 批次账号,密钥跟批次号一对一关系
BasUrl string `validate:"required" json:"base_url"` // 请求地址
NotifyUrl string `validate:"required" json:"notify_url"` // 回调地址
Official Official `validate:"required" json:"official"` // 公众号信息
MiniProgram MiniProgram `json:"mini_program"` // 小程序信息
}
func (c *Config) validate() error {
if err := validator.New().Struct(c); err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
}
}
if c.Mode.IsRedPack() {
return nil
}
return proto.ErrorConfigFail("微信发放红包模式不正确")
}
func transConfig(config []byte) (*Config, error) {
var c Config
if err := json.Unmarshal(config, &c); err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("配置参数解析失败: %v", err))
}
if err := c.validate(); err != nil {
return nil, proto.ErrorConfigFail(err.Error())
}
return &c, nil
}
func (c *Config) orderReq(order *proto.OrderRequest_Order, product *proto.OrderRequest_Product) (*po.OrderReq, error) {
var productExtra struct {
BatchName string `json:"batch_name"`
BatchRemark string `json:"batch_remark"`
GoodsName string `json:"goods_name"`
}
if err := json.Unmarshal(product.Extra, &productExtra); err != nil {
return nil, proto.ErrorParamFail(fmt.Sprintf("product拓展参数解析失败: %v", err))
}
return &po.OrderReq{
Amount: order.Amount,
BatchId: c.BatchId,
BizNo: order.OrderNo,
WxAppId: c.Official.AppId,
OpenId: order.Account,
SendName: productExtra.GoodsName,
RedPacketName: productExtra.GoodsName,
Wishing: productExtra.BatchName,
Remark: productExtra.BatchRemark,
NotifyUrl: c.NotifyUrl,
Sign: "",
}, nil
}
func orderResp(request *proto.OrderRequest, resp *po.OrderResp, bodyBytes []byte) *proto.OrderResponse {
return &proto.OrderResponse{
Result: &proto.Result{
Status: resp.GetOrderStatus(),
OrderNo: request.Order.OrderNo,
TradeNo: "",
Message: resp.GetMsg(),
Data: bodyBytes,
},
}
}
func (c *Config) queryReq(in *proto.QueryRequest_Order) *po.QueryReq {
return &po.QueryReq{
BizNo: in.OrderNo,
BatchId: c.BatchId,
}
}
func queryResp(request *proto.QueryRequest, resp *po.QueryResp, bodyBytes []byte) *proto.QueryResponse {
return &proto.QueryResponse{
Result: &proto.Result{
OrderNo: request.Order.OrderNo,
TradeNo: "",
Status: resp.GetOrderStatus(),
Message: resp.Msg,
Data: bodyBytes,
},
}
}
func notifyReq(in *proto.NotifyRequest) (*po.Notify, error) {
var n *po.Notify
if err := json.Unmarshal(in.Body, &n); err != nil {
return nil, proto.ErrorParamFail(fmt.Sprintf("notify拓展参数解析失败: %v", err))
}
if err := n.Validate(); err != nil {
return nil, proto.ErrorParamFail(fmt.Sprintf("notify参数有误:%s", err.Error()))
}
return n, nil
}
func notifyResp(n *po.Notify, data []byte) (*proto.NotifyResponse, error) {
pb := &proto.NotifyResponse{
Result: &proto.Result{
Status: n.Type.GetOrderType(),
OrderNo: n.BizNo,
TradeNo: "",
Message: n.FailMsg,
Data: data,
Extra: nil,
},
Return: `{"code":"success"}`,
}
if pb.Result.Status == proto.Status_SUCCESS {
pb.Result.Message = `成功`
}
headers := make(http.Header)
headers.Set("Content-Type", "application/json")
headersBytes, err := json.Marshal(headers)
if err != nil {
return nil, err
}
pb.Headers = string(headersBytes)
return pb, nil
}

View File

@ -1,118 +0,0 @@
package internal
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"gitea.cdlsxd.cn/sdk/plugin/utils"
"net/http"
"net/url"
"plugins/qixing_wechat_redpack/internal/po"
"plugins/utils/request"
"strings"
)
func req(config *Config, req po.Req) ([]byte, error) {
if err := req.Validate(); err != nil {
return nil, err
}
var strToBeSigned strings.Builder
kvRows := utils.SortStructJsonTag(req)
for _, kv := range kvRows {
if kv.Key == "sign" || kv.Value == "" {
continue
}
strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
}
s := strToBeSigned.String() + config.AppKey
req.SetSign(MD5(s))
return req.ToJson()
}
func getReq(req po.Req) (url.Values, error) {
if err := req.Validate(); err != nil {
return nil, proto.ErrorParamFail(err.Error())
}
uv := url.Values{}
kvRows := utils.SortStructJsonTag(req)
for _, kv := range kvRows {
if kv.Key == "sign" || kv.Value == "" {
continue
}
uv.Set(kv.Key, kv.Value)
}
return uv, nil
}
func Post(ctx context.Context, url string, reqBody []byte) ([]byte, http.Header, error) {
h := http.Header{
"Content-Type": []string{"application/json"},
}
respHeader, respBody, err := request.Post(ctx, url, reqBody, request.WithHeaders(h))
if err != nil {
return nil, nil, proto.ErrorRequestFail(err.Error())
}
return respBody, respHeader, nil
}
func Get(ctx context.Context, url string, uv url.Values) ([]byte, http.Header, error) {
requestUrl := url
if uv != nil {
requestUrl = url + "?" + uv.Encode()
}
h := http.Header{
"Content-Type": []string{"application/x-www-form-urlencoded"},
}
respHeader, respBody, err := request.Get(ctx, requestUrl, request.WithHeaders(h))
if err != nil {
return nil, nil, proto.ErrorRequestFail(err.Error())
}
return respBody, respHeader, nil
}
func MD5(data string) string {
// 创建一个 MD5 哈希对象
hash := md5.New()
// 写入待加密的数据
hash.Write([]byte(data))
// 获取 MD5 哈希值
hashBytes := hash.Sum(nil)
// 将 MD5 哈希值转换为16进制字符串
return hex.EncodeToString(hashBytes)
}
func Verify(n *po.Notify, appKey string) bool {
var strToBeSigned strings.Builder
uv := url.Values{}
kvRows := utils.SortStructJsonTag(n)
for _, kv := range kvRows {
if kv.Key == "sign" || kv.Value == "" {
continue
}
uv.Set(kv.Key, kv.Value)
strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
}
s := strToBeSigned.String() + appKey
return MD5(s) == n.Sign
}

View File

@ -1,23 +0,0 @@
package vo
type Code string
var CodeTextMap = map[Code]string{
"000000": "成功",
"100000": "内部错误(联系技术支持)",
"100001": "验签错误(检查签名算法)",
"100010": "活动批次不存在(检查batchId)",
"100011": "活动预算不足(联系客服增加预算)",
"100012": "业务编号已存在(更换bizNo)",
"100014": "缺少请求参数(检查必填字段)",
"100015": "发放错误(检查金额/账户信息)",
"100088": "接口请求频繁(重新调用接口)",
}
func (o Code) GetText() string {
msg, ok := CodeTextMap[o]
if !ok {
return ""
}
return msg
}

View File

@ -1,29 +0,0 @@
package vo
type Mode string
const (
ModeRedPack = "wechat_redpack_mode"
ModeTransfer = "wechat_transfer_mode"
)
var ModeTextMap = map[Mode]string{
ModeRedPack: "微信红包模式",
ModeTransfer: "微信转账模式",
}
func (o Mode) GetText() string {
msg, ok := ModeTextMap[o]
if !ok {
return ""
}
return msg
}
func (o Mode) GetMode() string {
return string(o)
}
func (o Mode) IsRedPack() bool {
return o == ModeRedPack
}

View File

@ -1,47 +0,0 @@
package vo
import "gitea.cdlsxd.cn/sdk/plugin/proto"
type Status string
const (
StatusUnclaimed = "0"
StatusReceived = "1"
StatusExpired = "2"
StatusFailed = "3"
)
var StatusTextMap = map[Status]string{
StatusUnclaimed: "未领取",
StatusReceived: "已领取",
StatusExpired: "已过期",
StatusFailed: "发放失败",
}
var StatusMap = map[Status]proto.Status{
StatusUnclaimed: proto.Status_ING,
StatusReceived: proto.Status_SUCCESS,
StatusExpired: proto.Status_FAIL,
StatusFailed: proto.Status_FAIL,
}
func (o Status) GetText() string {
msg, ok := StatusTextMap[o]
if !ok {
return ""
}
return msg
}
func (o Status) GetOrderStatus() proto.Status {
if o == "" {
return proto.Status_INVALID
}
if resultStatus, ok := StatusMap[o]; ok {
return resultStatus
}
return proto.Status_INVALID
}

View File

@ -1,47 +0,0 @@
package vo
import "gitea.cdlsxd.cn/sdk/plugin/proto"
type Type string
const (
TypeSent = "SENT"
TypeReceived = "RECEIVED"
TypeRefund = "REFUND"
TypeFailed = "FAILED"
)
var TypeTextMap = map[Type]string{
TypeSent: "下单成功,领取中",
TypeReceived: "领取成功",
TypeRefund: "已过期退回",
TypeFailed: "领取失败",
}
var TypeMap = map[Type]proto.Status{
TypeSent: proto.Status_ING,
TypeReceived: proto.Status_SUCCESS,
TypeRefund: proto.Status_FAIL,
TypeFailed: proto.Status_FAIL,
}
func (o Type) GetText() string {
msg, ok := TypeTextMap[o]
if !ok {
return ""
}
return msg
}
func (o Type) GetOrderType() proto.Status {
if o == "" {
return proto.Status_INVALID
}
if resultType, ok := TypeMap[o]; ok {
return resultType
}
return proto.Status_INVALID
}

View File

@ -1,15 +0,0 @@
package main
import (
"gitea.cdlsxd.cn/sdk/plugin/shared"
"github.com/hashicorp/go-plugin"
"plugins/qixing_wechat_redpack/internal"
)
func main() {
plugin.Serve(&plugin.ServeConfig{
HandshakeConfig: shared.HandshakeConfig(internal.Version, internal.CookieKey, internal.CookieValue),
Plugins: shared.PluginSet(shared.NewPlugin(&internal.QiXingWechatRedPackService{}, internal.Tag)),
GRPCServer: plugin.DefaultGRPCServer,
})
}

View File

@ -5,10 +5,11 @@ go 1.22.2
replace plugins/utils => ../../utils
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.27
gitea.cdlsxd.cn/sdk/plugin v1.0.6
github.com/carlmjohnson/requests v0.24.2
github.com/go-playground/validator/v10 v10.22.0
github.com/hashicorp/go-plugin v1.6.1
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.9.0
plugins/utils v0.0.0-00010101000000-000000000000
)
@ -16,7 +17,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect

View File

@ -1,9 +1,13 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
gitea.cdlsxd.cn/sdk/plugin v1.0.27 h1:ocp+3n8qLlPm+yI0VpMXFWfC8gsdDrKxEowuD58T6CE=
gitea.cdlsxd.cn/sdk/plugin v1.0.27/go.mod h1:4fLMp/xB9GEBa3nJi62kXpHh7wnb9Lwjf0I8Vjaasx0=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca h1:snL161P7OynMA8hRVMLDjwnzZA2Q4mePg/iT/dyIfzA=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
gitea.cdlsxd.cn/sdk/plugin v1.0.6 h1:fcQrgdRT4zVmxqK8rsB4Oo7jnhnXDqsgQ8GagR+DSic=
gitea.cdlsxd.cn/sdk/plugin v1.0.6/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/carlmjohnson/requests v0.24.2 h1:JDakhAmTIKL/qL/1P7Kkc2INGBJIkIFP6xUeUmPzLso=
github.com/carlmjohnson/requests v0.24.2/go.mod h1:duYA/jDnyZ6f3xbcF5PpZ9N8clgopubP2nK5i6MVMhU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
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=
@ -16,8 +20,6 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-kratos/kratos/v2 v2.8.2 h1:EsEA7AmPQ2YQQ0FZrDWO2HgBNqeWM8z/mWKzS5UkQaQ=
github.com/go-kratos/kratos/v2 v2.8.2/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -53,10 +55,6 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
@ -71,11 +69,9 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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/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/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -137,9 +133,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -3,7 +3,6 @@ package po
import (
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/go-playground/validator/v10"
)
@ -21,7 +20,7 @@ func (req *OrderReq) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorParamFail(fmt.Sprintf("参数有误: %v", err))
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
@ -40,7 +39,7 @@ func (req *QueryReq) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorParamFail(fmt.Sprintf("参数有误: %v", err))
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
@ -59,7 +58,7 @@ func (req *Notify) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorParamFail(fmt.Sprintf("参数有误: %v", err))
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil

View File

@ -25,8 +25,5 @@ type QueryResp struct {
}
func (o *QueryResp) GetMsg() string {
if o.Code.IsSuccess() {
return o.Msg
}
return fmt.Sprintf("Msg:[%s],SubMsg:[%s],自定处理msg:[%s]", o.Msg, o.SubMsg, o.SubCode.GetMsg())
}

View File

@ -1,125 +1,47 @@
package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
sdkutils "gitea.cdlsxd.cn/sdk/plugin/utils"
"github.com/go-playground/validator/v10"
"net/http"
"plugins/union_pay_cpn/internal/po"
"plugins/union_pay_cpn/internal/vo"
"plugins/utils/request"
"plugins/utils/union_pay"
"strings"
"time"
)
type Config struct {
AppId string `validate:"required" json:"app_id"`
ChNlId string `validate:"required" json:"chnlId"` // 渠道方代码
IV string `validate:"required" json:"iv"` // 加密密钥
KEY string `validate:"required" json:"key"` // 加密密钥
Prk string `validate:"required" json:"prk"` // 私钥
Npk string `validate:"required" json:"npk"` // 回调公钥
}
func (c *Config) validate() error {
err := validator.New().Struct(c)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
}
}
return nil
AppId string `json:"app_id"`
ChNlId string `json:"chnlId"` // 渠道方代码
IV string `json:"iv"` // 加密密钥
KEY string `json:"key"` // 加密密钥
Prk string `json:"prk"` // 私钥
Npk string `json:"npk"` // 回调公钥
}
func transConfig(config []byte) (*Config, error) {
var c Config
if err := json.Unmarshal(config, &c); err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
}
if err := c.validate(); err != nil {
err := json.Unmarshal(config, &c)
if err != nil {
return nil, err
}
return &c, nil
}
func (c *Config) verify(req *po.Notify, notifyBizMethod string) error {
if req.Headers.SignMethod != vo.SignMethod {
return fmt.Errorf("签名方式不匹配")
}
if req.Headers.AppId != c.AppId {
return fmt.Errorf("appId不匹配")
}
if req.Headers.BizMethod != notifyBizMethod {
return fmt.Errorf("业务方法不匹配")
}
rehash := union_pay.Sha(req.Headers.Version, c.AppId, req.Headers.BizMethod, req.GetReId(), string(req.ToJson()))
lowerStr := strings.ToLower(rehash)
if union_pay.Verify(lowerStr, req.Headers.Sign, []byte(sdkutils.NewPublic().Build(c.Npk))) {
return nil
}
return fmt.Errorf("验签失败")
}
func (c *Config) headers(bizMethod string, req po.Req) http.Header {
h := make(http.Header)
h.Add("Content-type", vo.ContentType)
h.Add("version", vo.Version)
h.Add("appType", vo.AppType)
h.Add("signMethod", vo.SignMethod)
h.Add("appId", c.AppId)
h.Add("bizMethod", bizMethod)
h.Add("reqId", req.GetReId())
now := time.Now()
milliseconds := now.Unix()*1000 + int64(now.Nanosecond())/1e6
h.Add("reqTs", fmt.Sprintf("%d", milliseconds))
rehash := union_pay.Sha(vo.Version, c.AppId, bizMethod, req.GetReId(), string(req.ToJson()))
signValue, err := union_pay.Sign(rehash, []byte(sdkutils.NewPrivate().Build(c.Prk)))
if err != nil {
return nil
}
h.Add("sign", signValue)
return h
}
func (c *Config) Request(ctx context.Context, req po.Req, method, bizMethod string) (http.Header, []byte, error) {
respHeader, respBody, err := request.Post(
ctx,
fmt.Sprintf("%s%s", baseUri, method),
req.ToJson(),
request.WithHeaders(c.headers(bizMethod, req)),
request.WithTimeout(15*time.Second),
request.WithStatusCodeFunc(func(code int) bool {
return code == http.StatusOK
}),
)
if err != nil {
return nil, nil, proto.ErrorRequestFail(err.Error())
}
return respHeader, respBody, nil
}
func (c *Config) orderReq(in *proto.OrderRequest) (*po.OrderReq, error) {
type OrderExtra struct {
OrderDt string `json:"orderDt"`
}
var e OrderExtra
if err := json.Unmarshal(in.Order.Extra, &e); err != nil {
return nil, fmt.Errorf("订单拓展参数fail: %w", err)
err := json.Unmarshal(in.Order.Extra, &e)
if err != nil {
return nil, fmt.Errorf("订单拓展参数 json unmarshal error: %v", err)
}
mobile, err := union_pay.Encrypt([]byte(in.Order.Account), []byte(c.KEY), []byte(c.IV))
if err != nil {
return nil, err
}
return &po.OrderReq{
Cmd: vo.OrderCmd,
AccessId: vo.AccessId,
@ -135,7 +57,7 @@ func (c *Config) orderReq(in *proto.OrderRequest) (*po.OrderReq, error) {
}, nil
}
func orderResp(request *proto.OrderRequest, resp po.OrderResp) (*proto.OrderResponse, error) {
func orderResp(request *proto.OrderRequest, resp po.OrderResp) *proto.OrderResponse {
data, _ := json.Marshal(resp)
result := &proto.Result{
OrderNo: request.Order.OrderNo,
@ -149,24 +71,22 @@ func orderResp(request *proto.OrderRequest, resp po.OrderResp) (*proto.OrderResp
} else {
result.Status = proto.Status_ING
}
return &proto.OrderResponse{Result: result}, nil
return &proto.OrderResponse{Result: result}
}
func queryReq(in *proto.QueryRequest, chNlId string) (*po.QueryReq, error) {
func queryReq(in *proto.QueryRequest, chNlId string) *po.QueryReq {
type OrderExtra struct {
OrigDate string `json:"origDate"`
}
var e OrderExtra
if err := json.Unmarshal(in.Order.Extra, &e); err != nil {
return nil, err
}
_ = json.Unmarshal(in.Order.Extra, &e)
return &po.QueryReq{
ChNlId: chNlId,
Cmd: vo.QueryCmd,
OrigQid: in.Order.OrderNo,
OrigDate: e.OrigDate,
TraceId: in.Order.OrderNo,
}, nil
}
}
func queryResp(request *proto.QueryRequest, resp po.QueryResp) *proto.QueryResponse {
@ -181,15 +101,11 @@ func queryResp(request *proto.QueryRequest, resp po.QueryResp) *proto.QueryRespo
return &proto.QueryResponse{Result: result}
}
func notifyReq(in *proto.NotifyRequest) (*po.Notify, error) {
func notifyReq(in *proto.NotifyRequest) *po.Notify {
var h po.Headers
if err := json.Unmarshal(in.Headers, &h); err != nil {
return nil, err
}
_ = json.Unmarshal(in.Headers, &h)
var body po.Body
if err := json.NewDecoder(strings.NewReader(string(in.Body))).Decode(&body); err != nil {
return nil, err
}
_ = json.NewDecoder(strings.NewReader(string(in.Body))).Decode(&body)
body.CouponNum = union_pay.ConvertToInt(body.CouponNum)
body.OrderAt = union_pay.ConvertToInt(body.OrderAt)
body.DiscountAt = union_pay.ConvertToInt(body.DiscountAt)
@ -200,11 +116,11 @@ func notifyReq(in *proto.NotifyRequest) (*po.Notify, error) {
return &po.Notify{
Headers: &h,
Body: &body,
}, nil
}
}
func notifyResp(request *proto.NotifyRequest, n *po.Notify) (*proto.NotifyResponse, error) {
pb := &proto.NotifyResponse{
func notifyResp(request *proto.NotifyRequest, n *po.Notify) *proto.NotifyResponse {
return &proto.NotifyResponse{
Result: &proto.Result{
OrderNo: n.Body.TransSeq,
TradeNo: n.Body.CouponCd,
@ -214,15 +130,4 @@ func notifyResp(request *proto.NotifyRequest, n *po.Notify) (*proto.NotifyRespon
},
Return: "success",
}
headers := make(http.Header)
headers.Set("Content-Type", "text/plain")
headersBytes, err := json.Marshal(headers)
if err != nil {
return nil, err
}
pb.Headers = string(headersBytes)
return pb, err
}

View File

@ -2,8 +2,9 @@ package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/carlmjohnson/requests"
"plugins/union_pay_cpn/internal/po"
)
@ -36,71 +37,56 @@ func (p *UnionPayCpnService) Order(ctx context.Context, request *proto.OrderRequ
if err != nil {
return nil, err
}
req, err := c.orderReq(request)
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
}
if err = req.Validate(); err != nil {
return nil, err
}
_, bodyBytes, err := c.Request(ctx, req, orderMethod, orderBizMethod)
uv, err := c.orderReq(request)
if err != nil {
return nil, err
}
if err = uv.Validate(); err != nil {
return nil, err
}
var response po.OrderResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
url := fmt.Sprintf("%s%s", baseUri, orderMethod)
err = requests.URL(url).Headers(headers(c, uv, orderBizMethod)).BodyJSON(uv).ToJSON(&response).Fetch(ctx)
if err != nil {
return nil, fmt.Errorf("请求异常msg:" + err.Error())
}
return orderResp(request, response)
return orderResp(request, response), nil
}
func (p *UnionPayCpnService) Query(ctx context.Context, request *proto.QueryRequest) (*proto.QueryResponse, error) {
c, err := transConfig(request.Config)
conf, err := transConfig(request.Config)
if err != nil {
return nil, err
}
req, err := queryReq(request, c.ChNlId)
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
}
if err = req.Validate(); err != nil {
uv := queryReq(request, conf.ChNlId)
if err = uv.Validate(); err != nil {
return nil, err
}
_, bodyBytes, err := c.Request(ctx, req, queryMethod, queryBizMethod)
if err != nil {
return nil, err
}
var response po.QueryResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
url := fmt.Sprintf("%s%s", baseUri, queryMethod)
err = requests.URL(url).Headers(headers(conf, uv, queryBizMethod)).BodyJSON(uv).ToJSON(&response).Fetch(ctx)
if err != nil {
return nil, fmt.Errorf("请求异常msg:" + err.Error())
}
return queryResp(request, response), nil
}
func (p *UnionPayCpnService) Notify(_ context.Context, request *proto.NotifyRequest) (*proto.NotifyResponse, error) {
req, err := notifyReq(request)
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
}
if err = req.Validate(); err != nil {
uv := notifyReq(request)
if err := uv.Validate(); err != nil {
return nil, err
}
c, err := transConfig(request.Config)
conf, err := transConfig(request.Config)
if err != nil {
return nil, err
}
if err = c.verify(req, notifyBizMethod); err != nil {
if err = verify(conf, uv, notifyBizMethod); err != nil {
return nil, err
}
return notifyResp(request, req)
return notifyResp(request, uv), nil
}

View File

@ -73,7 +73,7 @@ func TestQuery(t *testing.T) {
t.Errorf("Query() error = %v", err)
return
}
t.Logf("%+v \n", got)
fmt.Printf("%+v \n", got)
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})
}

View File

@ -0,0 +1,55 @@
package internal
import (
"fmt"
sdkutils "gitea.cdlsxd.cn/sdk/plugin/utils"
"net/http"
"plugins/union_pay_cpn/internal/po"
"plugins/union_pay_cpn/internal/vo"
"plugins/utils/union_pay"
"strings"
"time"
)
func headers(config *Config, req po.Req, bizMethod string) map[string][]string {
h := make(http.Header)
h.Add("Content-type", vo.ContentType)
h.Add("version", vo.Version)
h.Add("appType", vo.AppType)
h.Add("signMethod", vo.SignMethod)
h.Add("appId", config.AppId)
h.Add("bizMethod", bizMethod)
h.Add("reqId", req.GetReId())
now := time.Now()
milliseconds := now.Unix()*1000 + int64(now.Nanosecond())/1e6
h.Add("reqTs", fmt.Sprintf("%d", milliseconds))
rehash := union_pay.Sha(vo.Version, config.AppId, bizMethod, req.GetReId(), string(req.ToJson()))
signValue, err := union_pay.Sign(rehash, []byte(sdkutils.NewPrivate().Build(config.Prk)))
if err != nil {
return nil
}
h.Add("sign", signValue)
return h
}
func verify(config *Config, req *po.Notify, notifyBizMethod string) error {
if req.Headers.SignMethod != vo.SignMethod {
return fmt.Errorf("签名方式不匹配")
}
if req.Headers.AppId != config.AppId {
return fmt.Errorf("appId不匹配")
}
if req.Headers.BizMethod != notifyBizMethod {
return fmt.Errorf("业务方法不匹配")
}
rehash := union_pay.Sha(req.Headers.Version, config.AppId, req.Headers.BizMethod, req.GetReId(), string(req.ToJson()))
lowerStr := strings.ToLower(rehash)
if union_pay.Verify(lowerStr, req.Headers.Sign, []byte(sdkutils.NewPublic().Build(config.Npk))) {
return nil
}
return fmt.Errorf("验签失败")
}

View File

@ -32,8 +32,11 @@ func (o OperaSt) GetText() string {
}
func (o OperaSt) GetOrderStatus() proto.Status {
if len(o) == 0 {
return proto.Status_INVALID
}
if resultStatus, ok := queryOrderStatusMap[o]; ok {
return resultStatus
}
return proto.Status_INVALID
return proto.Status_FAIL
}

View File

@ -5,10 +5,11 @@ go 1.22.2
replace plugins/utils => ../../utils
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.27
gitea.cdlsxd.cn/sdk/plugin v1.0.6
github.com/carlmjohnson/requests v0.24.2
github.com/go-playground/validator/v10 v10.22.0
github.com/hashicorp/go-plugin v1.6.1
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.9.0
plugins/utils v0.0.0-00010101000000-000000000000
)
@ -16,7 +17,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect

View File

@ -1,9 +1,13 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
gitea.cdlsxd.cn/sdk/plugin v1.0.27 h1:ocp+3n8qLlPm+yI0VpMXFWfC8gsdDrKxEowuD58T6CE=
gitea.cdlsxd.cn/sdk/plugin v1.0.27/go.mod h1:4fLMp/xB9GEBa3nJi62kXpHh7wnb9Lwjf0I8Vjaasx0=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca h1:snL161P7OynMA8hRVMLDjwnzZA2Q4mePg/iT/dyIfzA=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
gitea.cdlsxd.cn/sdk/plugin v1.0.6 h1:fcQrgdRT4zVmxqK8rsB4Oo7jnhnXDqsgQ8GagR+DSic=
gitea.cdlsxd.cn/sdk/plugin v1.0.6/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/carlmjohnson/requests v0.24.2 h1:JDakhAmTIKL/qL/1P7Kkc2INGBJIkIFP6xUeUmPzLso=
github.com/carlmjohnson/requests v0.24.2/go.mod h1:duYA/jDnyZ6f3xbcF5PpZ9N8clgopubP2nK5i6MVMhU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
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=
@ -16,8 +20,6 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-kratos/kratos/v2 v2.8.2 h1:EsEA7AmPQ2YQQ0FZrDWO2HgBNqeWM8z/mWKzS5UkQaQ=
github.com/go-kratos/kratos/v2 v2.8.2/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -53,10 +55,6 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
@ -71,11 +69,9 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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/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/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -137,9 +133,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -26,8 +26,5 @@ type QueryResp struct {
}
func (o *QueryResp) GetMsg() string {
if o.Code.IsSuccess() {
return o.Msg
}
return fmt.Sprintf("Msg:[%s],SubMsg:[%s],自定处理msg:[%s]", o.Msg, o.SubMsg, o.SubCode.GetMsg())
}

View File

@ -1,95 +1,35 @@
package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
sdkutils "gitea.cdlsxd.cn/sdk/plugin/utils"
"github.com/go-playground/validator/v10"
"net/http"
"plugins/union_pay_redpack/internal/po"
"plugins/union_pay_redpack/internal/vo"
"plugins/utils/request"
"plugins/utils/union_pay"
"time"
)
type Config struct {
AppId string `validate:"required" json:"app_id"`
ChNlId string `validate:"required" json:"chnlId"` // 渠道方代码
IV string `validate:"required" json:"iv"` // 加密密钥
KEY string `validate:"required" json:"key"` // 加密密钥
Prk string `validate:"required" json:"prk"` // 私钥
Npk string `validate:"required" json:"npk"` // 回调公钥
AppId string `json:"app_id"`
ChNlId string `json:"chnlId"` // 渠道方代码
IV string `json:"iv"` // 加密密钥
KEY string `json:"key"` // 加密密钥
Prk string `json:"prk"` // 私钥
Npk string `json:"npk"` // 回调公钥
PointId string `validate:"required" json:"point_id"` // 积分ID-积分类别代码41开头的16位数字
InsAcctId string `validate:"required" json:"ins_acct_id"` // 机构账户代码
}
func (c *Config) validate() error {
err := validator.New().Struct(c)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
}
}
return nil
PointId string `json:"point_id"` // 积分ID-积分类别代码41开头的16位数字
InsAcctId string `json:"ins_acct_id"` // 机构账户代码
}
func transConfig(config []byte) (*Config, error) {
var c Config
if err := json.Unmarshal(config, &c); err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
}
if err := c.validate(); err != nil {
err := json.Unmarshal(config, &c)
if err != nil {
return nil, err
}
return &c, nil
}
func (c *Config) headers(bizMethod string, req po.Req) http.Header {
h := make(http.Header)
h.Add("Content-type", vo.ContentType)
h.Add("version", vo.Version)
h.Add("appType", vo.AppType)
h.Add("signMethod", vo.SignMethod)
h.Add("appId", c.AppId)
h.Add("bizMethod", bizMethod)
h.Add("reqId", req.GetReId())
now := time.Now()
milliseconds := now.Unix()*1000 + int64(now.Nanosecond())/1e6
h.Add("reqTs", fmt.Sprintf("%d", milliseconds))
rehash := union_pay.Sha(vo.Version, c.AppId, bizMethod, req.GetReId(), string(req.ToJson()))
signValue, err := union_pay.Sign(rehash, []byte(sdkutils.NewPrivate().Build(c.Prk)))
if err != nil {
return nil
}
h.Add("sign", signValue)
return h
}
func (c *Config) Request(ctx context.Context, req po.Req, method, bizMethod string) (http.Header, []byte, error) {
respHeader, respBody, err := request.Post(
ctx,
fmt.Sprintf("%s%s", baseUri, method),
req.ToJson(),
request.WithHeaders(c.headers(bizMethod, req)),
request.WithTimeout(15*time.Second),
request.WithStatusCodeFunc(func(code int) bool {
return code == http.StatusOK
}),
)
if err != nil {
return nil, nil, proto.ErrorRequestFail(err.Error())
}
return respHeader, respBody, nil
}
func (c *Config) orderReq(in *proto.OrderRequest) (*po.OrderReq, error) {
type OrderExtra struct {
TransDtTm string `json:"transDtTm"` // 交易日期时间
@ -97,8 +37,9 @@ func (c *Config) orderReq(in *proto.OrderRequest) (*po.OrderReq, error) {
TransDigest string `json:"transDigest"` // 交易摘要
}
var e OrderExtra
if err := json.Unmarshal(in.Order.Extra, &e); err != nil {
return nil, fmt.Errorf("order extra fail: %v", err)
err := json.Unmarshal(in.Order.Extra, &e)
if err != nil {
return nil, fmt.Errorf("order extra json unmarshal error: %v", err)
}
mobile, err := union_pay.Encrypt([]byte(in.Order.Account), []byte(c.KEY), []byte(c.IV))
@ -150,8 +91,9 @@ func (c *Config) queryReq(in *proto.QueryRequest, chNlId string) (*po.QueryReq,
OrigTransDtTm string `json:"origTransDtTm"` // 原交易日期时间
}
var e OrderExtra
if err := json.Unmarshal(in.Order.Extra, &e); err != nil {
return nil, fmt.Errorf("order extra fail: %v", err)
err := json.Unmarshal(in.Order.Extra, &e)
if err != nil {
return nil, fmt.Errorf("order extra json unmarshal error: %v", err)
}
mobile, err := union_pay.Encrypt([]byte(in.Order.Account), []byte(c.KEY), []byte(c.IV))
if err != nil {

View File

@ -2,9 +2,12 @@ package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/carlmjohnson/requests"
"net/http"
"plugins/union_pay_redpack/internal/po"
"time"
)
// 插件通信信息,若不对应则会报错panic
@ -34,22 +37,17 @@ func (p *UnionPayCpnService) Order(ctx context.Context, request *proto.OrderRequ
if err != nil {
return nil, err
}
req, err := c.orderReq(request)
uv, err := c.orderReq(request)
if err != nil {
return nil, err
}
if err = req.Validate(); err != nil {
if err = uv.Validate(); err != nil {
return nil, err
}
_, bodyBytes, err := c.Request(ctx, req, orderMethod, orderBizMethod)
if err != nil {
return nil, err
}
var response po.OrderResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
err = requests.URL(baseUri + orderMethod).Headers(headers(c, uv, orderBizMethod)).Post().BodyJSON(uv).ToJSON(&response).Fetch(ctx)
if err != nil {
return nil, fmt.Errorf("请求异常msg:" + err.Error())
}
return orderResp(request, response), nil
@ -60,22 +58,20 @@ func (p *UnionPayCpnService) Query(ctx context.Context, request *proto.QueryRequ
if err != nil {
return nil, err
}
req, err := c.queryReq(request, c.ChNlId)
uv, err := c.queryReq(request, c.ChNlId)
if err != nil {
return nil, err
}
if err = req.Validate(); err != nil {
if err = uv.Validate(); err != nil {
return nil, err
}
_, bodyBytes, err := c.Request(ctx, req, queryMethod, queryBizMethod)
if err != nil {
return nil, err
}
var response po.QueryResp
if err = json.Unmarshal(bodyBytes, &response); err != nil {
return nil, proto.ErrorResponseFail(err.Error())
h := new(http.Client)
h.Timeout = 20 * time.Second
err = requests.URL(baseUri + queryMethod).Client(h).Headers(headers(c, uv, queryBizMethod)).BodyJSON(uv).ToJSON(&response).Fetch(ctx)
if err != nil {
return nil, fmt.Errorf("请求异常msg:" + err.Error())
}
return queryResp(request, response), nil

View File

@ -74,6 +74,8 @@ func TestOrder(t *testing.T) {
t.Errorf("Order() error = %v", err)
return
}
fmt.Printf("request order:%+v \n", request.Order)
fmt.Printf("request product:%+v \n", request.Product)
fmt.Printf("got:%+v \n", got)
assert.Equal(t, int(proto.Status_ING), int(got.Result.Status))
})

View File

@ -0,0 +1,36 @@
package internal
import (
"fmt"
sdkutils "gitea.cdlsxd.cn/sdk/plugin/utils"
"net/http"
"plugins/union_pay_redpack/internal/po"
"plugins/union_pay_redpack/internal/vo"
"plugins/utils/union_pay"
"time"
)
func headers(config *Config, req po.Req, bizMethod string) map[string][]string {
h := make(http.Header)
h.Add("Content-type", vo.ContentType)
h.Add("version", vo.Version)
h.Add("appType", vo.AppType)
h.Add("signMethod", vo.SignMethod)
h.Add("appId", config.AppId)
h.Add("bizMethod", bizMethod)
h.Add("reqId", req.GetReId())
now := time.Now()
milliseconds := now.Unix()*1000 + int64(now.Nanosecond())/1e6
h.Add("reqTs", fmt.Sprintf("%d", milliseconds))
rehash := union_pay.Sha(vo.Version, config.AppId, bizMethod, req.GetReId(), string(req.ToJson()))
signValue, err := union_pay.Sign(rehash, []byte(sdkutils.NewPrivate().Build(config.Prk)))
if err != nil {
return nil
}
h.Add("sign", signValue)
return h
}

View File

@ -5,9 +5,9 @@ go 1.22.2
replace plugins/utils => ../../utils
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.27
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca
github.com/hashicorp/go-plugin v1.6.1
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.9.0
github.com/wechatpay-apiv3/wechatpay-go v0.2.18
plugins/utils v1.0.0
)
@ -16,7 +16,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.0 // indirect

View File

@ -1,5 +1,5 @@
gitea.cdlsxd.cn/sdk/plugin v1.0.27 h1:ocp+3n8qLlPm+yI0VpMXFWfC8gsdDrKxEowuD58T6CE=
gitea.cdlsxd.cn/sdk/plugin v1.0.27/go.mod h1:4fLMp/xB9GEBa3nJi62kXpHh7wnb9Lwjf0I8Vjaasx0=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca h1:snL161P7OynMA8hRVMLDjwnzZA2Q4mePg/iT/dyIfzA=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
@ -11,8 +11,6 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-kratos/kratos/v2 v2.8.2 h1:EsEA7AmPQ2YQQ0FZrDWO2HgBNqeWM8z/mWKzS5UkQaQ=
github.com/go-kratos/kratos/v2 v2.8.2/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -33,10 +31,6 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
@ -50,8 +44,6 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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=
@ -59,8 +51,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/wechatpay-apiv3/wechatpay-go v0.2.18 h1:vj5tvSmnEIz3ZsnFNNUzg+3Z46xgNMJbrO4aD4wP15w=
github.com/wechatpay-apiv3/wechatpay-go v0.2.18/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
@ -79,9 +71,8 @@ google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -6,63 +6,34 @@ import (
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/services/cashcoupons"
"go/types"
"plugins/utils/wechat"
"plugins/wechat_cpn/internal/vo"
)
type Extra struct {
Appid string `json:"app_id"`
StockCreatorMchId string `json:"stock_creator_mchid"`
}
func transConfig(config []byte) (*wechat.Server, error) {
var c wechat.Server
if err := json.Unmarshal(config, &c); err != nil {
return nil, proto.ErrorConfigFail(err.Error())
}
if err := c.Validate(); err != nil {
err := json.Unmarshal(config, &c)
if err != nil {
return nil, err
}
if err = c.Validate(); err != nil {
return nil, err
}
return &c, nil
}
func orderReq(order *proto.OrderRequest_Order, product *proto.OrderRequest_Product) (cashcoupons.SendCouponRequest, error) {
if order.Extra == nil {
return cashcoupons.SendCouponRequest{}, fmt.Errorf("order拓展参数不能为空")
type Extra struct {
Appid string `json:"app_id"`
StockCreatorMchId string `json:"stock_creator_mchid"`
}
var extra Extra
if order.Extra != nil {
if err := json.Unmarshal(order.Extra, &extra); err != nil {
return cashcoupons.SendCouponRequest{}, fmt.Errorf("order拓展参数 json unmarshal error: %v", err)
}
}
if extra.Appid == "" {
return cashcoupons.SendCouponRequest{}, fmt.Errorf("order拓展参数 app_id 不能为空")
}
if extra.StockCreatorMchId == "" {
return cashcoupons.SendCouponRequest{}, fmt.Errorf("order拓展参数 stock_creator_mchid 不能为空")
}
if product.ProductNo == "" {
return cashcoupons.SendCouponRequest{}, fmt.Errorf("product产品编号不能为空")
}
if order.Account == "" {
return cashcoupons.SendCouponRequest{}, fmt.Errorf("order用户账号不能为空")
}
if order.OrderNo == "" {
return cashcoupons.SendCouponRequest{}, fmt.Errorf("order订单编号不能为空")
}
return cashcoupons.SendCouponRequest{
Openid: core.String(order.Account),
StockId: core.String(product.ProductNo),
@ -118,6 +89,10 @@ func queryResp(request *proto.QueryRequest, resp *cashcoupons.Coupon) *proto.Que
}
}
func notifyReq(in *proto.NotifyRequest) *types.Nil {
return nil
}
func notifyResp() *proto.NotifyResponse {
return &proto.NotifyResponse{
Result: &proto.Result{

View File

@ -8,29 +8,18 @@ const (
QueryStatusAvailable = "SENDED"
QueryStatusUsed = "USED"
QueryStatusExpired = "EXPIRED"
// StatusRevoked 当代金券被商户主动撤销(作废)时,会触发“已失效”状态,用户无法再使用。此情况下,代金券状态不可逆,
// 需重新发放才能再次使用。建议通过查询接口确认代金券状态,并确保在有效期内合理管理券的发放与使用。
StatusRevoked = "REVOKED" // 代金券被商户主动撤销。
StatusRecover = "RECOVER" // 代金券被系统回收,可能因未使用或规则触发
)
var queryStatusTextMap = map[QueryStatus]string{
QueryStatusAvailable: "可用",
QueryStatusUsed: "已实扣",
QueryStatusExpired: "已过期",
StatusRevoked: "已失效(代金券被商户主动撤销)",
StatusRecover: "已回收(代金券被系统回收,可能因未使用或规则触发)",
}
var queryStatusMap = map[QueryStatus]proto.Status{
QueryStatusAvailable: proto.Status_SUCCESS,
QueryStatusUsed: proto.Status_WRITE_OFF,
QueryStatusExpired: proto.Status_OVERDUE,
StatusRevoked: proto.Status_REVOKE,
StatusRecover: proto.Status_INVALID,
}
func (o QueryStatus) GetText() string {

View File

@ -9,7 +9,6 @@ import (
)
// 插件通信信息,若不对应则会报错panic
// 微信立减金文档地址: https://pay.weixin.qq.com/doc/v3/merchant/4012463767
const (
Tag = "wechat_cpn"
Version = 1
@ -19,120 +18,69 @@ const (
type WeChatCpnService struct{}
func (p *WeChatCpnService) Order(ctx context.Context, request *proto.OrderRequest) (resp2 *proto.OrderResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("插件panic: %v", err))
}
}()
func (p *WeChatCpnService) Order(ctx context.Context, request *proto.OrderRequest) (*proto.OrderResponse, error) {
config, err := transConfig(request.Config)
if err != nil {
return nil, err
}
req, err := orderReq(request.GetOrder(), request.GetProduct())
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
return nil, err
}
svc, err := srv(ctx, config)
if err != nil {
return nil, proto.ErrorRequestFail(err.Error())
return nil, err
}
resp, result, err := svc.SendCoupon(ctx, req)
if err != nil {
return nil, proto.ErrorRequestFail(fmt.Sprintf("微信返回错误:%s", p.err(ctx, err)))
return nil, fmt.Errorf("微信返回错误 %v", p.err(ctx, err))
}
if result == nil {
return nil, proto.ErrorRequestFail("微信返回错误:result为空")
}
if result.Response == nil {
return nil, proto.ErrorRequestFail("微信返回错误:result.Response为空")
}
if result.Response.StatusCode != vo.CodeSuccess.Value() {
return nil, proto.ErrorRequestFail(fmt.Sprintf("微信返回错误:[%d-%s]", result.Response.StatusCode, result.Response.Status))
return nil, fmt.Errorf("微信返回错误 StatusCode[%d]Status[%s]", result.Response.StatusCode, result.Response.Status)
}
if resp == nil {
return nil, proto.ErrorRequestFail("微信返回错误:resp为空")
}
return orderResp(request.GetOrder(), *resp.CouponId), nil
}
func (p *WeChatCpnService) Query(ctx context.Context, request *proto.QueryRequest) (resp2 *proto.QueryResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("插件panic: %v", err))
}
}()
func (p *WeChatCpnService) Query(ctx context.Context, request *proto.QueryRequest) (*proto.QueryResponse, error) {
config, err := transConfig(request.Config)
if err != nil {
return nil, err
}
svc, err := srv(ctx, config)
if err != nil {
return nil, err
}
req, err := queryReq(request.GetOrder())
if err != nil {
return nil, err
}
resp, result, err := svc.QueryCoupon(ctx, *req)
if err != nil {
return nil, proto.ErrorRequestFail(fmt.Sprintf("微信返回错误:%s", p.err(ctx, err)))
return nil, p.err(ctx, err)
}
if result.Response.StatusCode != vo.CodeSuccess.Value() {
return nil, proto.ErrorRequestFail(fmt.Sprintf("微信返回错误StatusCode[%d]Status[%s]", result.Response.StatusCode, result.Response.Status))
return nil, fmt.Errorf("微信返回错误 StatusCode[%d]Status[%s]", result.Response.StatusCode, result.Response.Status)
}
return queryResp(request, resp), nil
}
func (p *WeChatCpnService) Notify(ctx context.Context, request *proto.NotifyRequest) (resp2 *proto.NotifyResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("插件panic: %v", err))
}
}()
func (p *WeChatCpnService) Notify(ctx context.Context, request *proto.NotifyRequest) (*proto.NotifyResponse, error) {
config, err := transConfig(request.Config)
if err != nil {
return nil, err
}
if err = verify(ctx, config); err != nil {
return nil, err
}
return notifyResp(), nil
}
func (p *WeChatCpnService) err(_ context.Context, err error) error {
errStr := err.Error()
startIndex := strings.Index(errStr, "Message: ")
if startIndex == -1 {
return err
}
endIndex := strings.Index(errStr[startIndex:], "\n")
if endIndex == -1 {
return err
}
return fmt.Errorf(errStr[startIndex+len("Message: ") : startIndex+endIndex])
}

View File

@ -15,7 +15,7 @@ var server = &WeChatCpnService{}
func config() []byte {
c := &wechat.Server{
MchID: "1605446142", // 证书所属商户
MchCertificateSerialNumber: "4D081089DEB385316CBDCB55C070287E4920AC766",
MchCertificateSerialNumber: "4D081089DEB385316CBDCB55C070287E4920AC76",
}
marshal, _ := json.Marshal(c)
return marshal
@ -53,24 +53,6 @@ func TestOrder(t *testing.T) {
})
}
func Test_Order(t *testing.T) {
var request *proto.OrderRequest
err := json.Unmarshal([]byte(`{"tag": "wechat_cpn", "order": {"extra": "", "account": "oO3vO5K2nE131-9uMoeYymLhlbYk", "order_no": "YMT202602041507084910010016", "quantity": 1}, "product": {"product_no": "21605443"}, "config": "{\"mch_id\": \"1605446142\", \"mch_certificate_serial_number\": \"46B64A9AF817BCE0425AB2ED003E7FC3C3DC48D9\"}"}`), &request)
if err != nil {
t.Errorf("json.Unmarshal() error = %v", err)
return
}
t.Run("Test_Order", func(t *testing.T) {
got, err := server.Order(context.Background(), request)
if err != nil {
t.Errorf("Order() error = %v", err)
return
}
fmt.Printf("%+v", got)
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})
}
func TestQuery(t *testing.T) {
request := &proto.QueryRequest{
Config: config(),

View File

@ -5,9 +5,9 @@ go 1.22.2
replace plugins/utils => ../../utils
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.27
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca
github.com/hashicorp/go-plugin v1.6.1
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.9.0
github.com/wechatpay-apiv3/wechatpay-go v0.2.18
plugins/utils v1.0.0
)
@ -16,7 +16,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.0 // indirect

View File

@ -1,5 +1,5 @@
gitea.cdlsxd.cn/sdk/plugin v1.0.27 h1:ocp+3n8qLlPm+yI0VpMXFWfC8gsdDrKxEowuD58T6CE=
gitea.cdlsxd.cn/sdk/plugin v1.0.27/go.mod h1:4fLMp/xB9GEBa3nJi62kXpHh7wnb9Lwjf0I8Vjaasx0=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca h1:snL161P7OynMA8hRVMLDjwnzZA2Q4mePg/iT/dyIfzA=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
@ -11,8 +11,6 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-kratos/kratos/v2 v2.8.2 h1:EsEA7AmPQ2YQQ0FZrDWO2HgBNqeWM8z/mWKzS5UkQaQ=
github.com/go-kratos/kratos/v2 v2.8.2/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -33,10 +31,6 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
@ -50,8 +44,6 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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=
@ -59,8 +51,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/wechatpay-apiv3/wechatpay-go v0.2.18 h1:vj5tvSmnEIz3ZsnFNNUzg+3Z46xgNMJbrO4aD4wP15w=
github.com/wechatpay-apiv3/wechatpay-go v0.2.18/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
@ -79,9 +71,8 @@ google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -14,7 +14,7 @@ func transConfig(config []byte) (*wechat.Server, error) {
var c wechat.Server
err := json.Unmarshal(config, &c)
if err != nil {
return nil, proto.ErrorConfigFail(err.Error())
return nil, err
}
if err = c.Validate(); err != nil {
return nil, err

View File

@ -19,68 +19,48 @@ const (
// WeChatRedPackService @link https://pay.weixin.qq.com/docs/merchant/apis/batch-transfer-to-balance/transfer-batch/initiate-batch-transfer.html
type WeChatRedPackService struct{}
func (p *WeChatRedPackService) Order(ctx context.Context, request *proto.OrderRequest) (resp2 *proto.OrderResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
func (p *WeChatRedPackService) Order(ctx context.Context, request *proto.OrderRequest) (*proto.OrderResponse, error) {
config, err := transConfig(request.Config)
if err != nil {
return nil, err
}
req, err := orderReq(request.GetOrder(), request.GetProduct())
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
return nil, err
}
svc, err := transferBatchApiService(ctx, config)
if err != nil {
return nil, proto.ErrorRequestFail(err.Error())
return nil, err
}
resp, result, err := svc.InitiateBatchTransfer(ctx, req)
if err != nil {
return nil, proto.ErrorRequestFail("微信返回错误:" + p.err(err).Error())
return nil, p.err(ctx, err)
}
if result.Response.StatusCode != vo.CodeSuccess.Value() {
return nil, proto.ErrorRequestFail(fmt.Sprintf("微信返回错误StatusCode[%d]Status[%s]", result.Response.StatusCode, result.Response.Status))
return nil, fmt.Errorf("微信返回错误 Response StatusCode[%d]Status[%s]", result.Response.StatusCode, result.Response.Status)
}
return orderResp(request.GetOrder(), *resp.BatchId), nil
}
func (p *WeChatRedPackService) Query(ctx context.Context, request *proto.QueryRequest) (resp2 *proto.QueryResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = proto.ErrorPanic(fmt.Sprintf("panic: %v", err))
}
}()
func (p *WeChatRedPackService) Query(ctx context.Context, request *proto.QueryRequest) (*proto.QueryResponse, error) {
req, err := queryReq(request.GetOrder())
if err != nil {
return nil, err
}
config, err := transConfig(request.Config)
if err != nil {
return nil, err
}
req, err := queryReq(request.GetOrder())
if err != nil {
return nil, proto.ErrorParamFail(err.Error())
}
svc, err := transferDetailApiService(ctx, config)
if err != nil {
return nil, proto.ErrorRequestFail(err.Error())
return nil, err
}
resp, result, err := svc.GetTransferDetailByOutNo(ctx, *req)
if err != nil {
return nil, proto.ErrorRequestFail("微信返回错误:" + p.err(err).Error())
return nil, p.err(ctx, err)
}
if result.Response.StatusCode != vo.CodeSuccess.Value() {
return nil, proto.ErrorRequestFail(fmt.Sprintf("微信返回错误StatusCode[%d]Status[%s]", result.Response.StatusCode, result.Response.Status))
return nil, fmt.Errorf("微信返回错误 Response StatusCode[%d]Status[%s]", result.Response.StatusCode, result.Response.Status)
}
return queryResp(request, resp), nil
}
@ -89,18 +69,15 @@ func (p *WeChatRedPackService) Notify(_ context.Context, _ *proto.NotifyRequest)
return notifyResp(), nil
}
func (p *WeChatRedPackService) err(err error) error {
func (p *WeChatRedPackService) err(_ context.Context, err error) error {
errStr := err.Error()
startIndex := strings.Index(errStr, "Message: ")
if startIndex == -1 {
return err
}
endIndex := strings.Index(errStr[startIndex:], "\n")
if endIndex == -1 {
return err
}
return fmt.Errorf(errStr[startIndex+len("Message: ") : startIndex+endIndex])
}

View File

@ -1,161 +0,0 @@
package internal
import (
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"plugins/wechat_redpack_v2/internal/wechat/srv/transfer"
"plugins/wechat_redpack_v2/internal/wechat/utils"
)
func transConfig(config []byte) (*Wechat, error) {
var c Wechat
if err := json.Unmarshal(config, &c); err != nil {
return nil, proto.ErrorConfigFail(err.Error())
}
if err := c.Validate(); err != nil {
return nil, proto.ErrorConfigFail(err.Error())
}
return &c, nil
}
type OrderExtra struct {
Appid string `json:"app_id"`
NotifyUrl string `json:"notify_url"`
}
type ProductExtra struct {
BatchName string `json:"batch_name"`
BatchRemark string `json:"batch_remark"`
}
func orderReq(order *proto.OrderRequest_Order, product *proto.OrderRequest_Product) (*transfer.TransferToUserRequest, error) {
var orderExtra OrderExtra
err := json.Unmarshal(order.Extra, &orderExtra)
if err != nil {
return nil, fmt.Errorf("order拓展参数 json unmarshal error[%+v]", err)
}
var productExtra ProductExtra
if err := json.Unmarshal(product.Extra, &productExtra); err != nil {
return nil, fmt.Errorf("product拓展参数json unmarshal error:[%+v], extra[%s]", err, string(product.Extra))
}
transferSceneReportInfos := []transfer.TransferSceneReportInfo{
{
InfoType: utils.String("活动名称"),
InfoContent: utils.String(productExtra.BatchName), // 商户自定义内容
},
{
InfoType: utils.String("奖励说明"),
InfoContent: utils.String(productExtra.BatchRemark), // 商户自定义内容
},
}
transferAmount := int64(order.Amount * 100)
return &transfer.TransferToUserRequest{
Appid: utils.String(orderExtra.Appid),
OutBillNo: utils.String(order.OrderNo),
TransferSceneId: utils.String("1000"),
Openid: utils.String(order.Account),
//UserName: utils.String(orderExtra.Appid),
TransferAmount: utils.Int64(transferAmount),
TransferRemark: utils.String(productExtra.BatchRemark),
NotifyUrl: utils.String(orderExtra.NotifyUrl),
//UserRecvPerception: utils.String(""),
TransferSceneReportInfos: transferSceneReportInfos,
}, nil
}
func orderResp(order *proto.OrderRequest_Order, response *transfer.TransferToUserResponse) (*proto.OrderResponse, error) {
if *response.State != transfer.TRANSFERBILLSTATUS_WAIT_USER_CONFIRM {
return nil, fmt.Errorf("微信转账异常:%s", response.State.GetText())
}
// 返回出去
data, _ := json.Marshal(response)
return &proto.OrderResponse{
Result: &proto.Result{
Status: proto.Status_ING,
OrderNo: order.GetOrderNo(),
TradeNo: *response.OutBillNo,
Message: "成功",
Data: data,
Extra: nil,
},
}, nil
}
func queryReq(order *proto.QueryRequest_Order) (*transfer.GetTransferBillByOutNoRequest, error) {
if order.OrderNo == "" {
return nil, fmt.Errorf("商户订单号不能为空")
}
return &transfer.GetTransferBillByOutNoRequest{
OutBillNo: core.String(order.OrderNo),
}, nil
}
func queryResp(request *proto.QueryRequest, response *transfer.TransferBillEntity) (*proto.QueryResponse, error) {
// 返回出去 转账完成或者转账失败对应单据状态status的值为SUCCESS、CANCELLED和FAIL
data, _ := json.Marshal(response)
return &proto.QueryResponse{
Result: &proto.Result{
Status: statusResp(*response.State),
OrderNo: request.Order.GetOrderNo(),
TradeNo: *response.TransferBillNo,
Message: response.State.GetText(),
Data: data,
Extra: nil,
},
}, nil
}
func notifyResp(response *transfer.TransferBillEntity) *proto.NotifyResponse {
// 返回出去 转账完成或者转账失败对应单据状态status的值为SUCCESS、CANCELLED和FAIL
data, _ := json.Marshal(response)
return &proto.NotifyResponse{
Result: &proto.Result{
Status: statusResp(*response.State),
OrderNo: *response.OutBillNo,
TradeNo: *response.TransferBillNo,
Message: response.State.GetText(),
Data: data,
Extra: nil,
},
Return: "",
}
}
func statusResp(responseStatus transfer.TransferBillStatus) proto.Status {
var status proto.Status
if responseStatus == transfer.TRANSFERBILLSTATUS_SUCCESS {
status = proto.Status_SUCCESS
} else if responseStatus == transfer.TRANSFERBILLSTATUS_PROCESSING {
status = proto.Status_ING
} else if responseStatus == transfer.TRANSFERBILLSTATUS_TRANSFERING {
status = proto.Status_ING
} else if responseStatus == transfer.TRANSFERBILLSTATUS_FAIL {
status = proto.Status_FAIL
} else if responseStatus == transfer.TRANSFERBILLSTATUS_CANCELING {
status = proto.Status_ING
} else if responseStatus == transfer.TRANSFERBILLSTATUS_CANCELLED {
status = proto.Status_FAIL
} else {
status = proto.Status_ING
}
return status
}

View File

@ -1,9 +0,0 @@
package vo
type Code int
const CodeSuccess Code = 200
func (c Code) Value() int {
return int(c)
}

View File

@ -1,32 +0,0 @@
package vo
import "github.com/wechatpay-apiv3/wechatpay-go/services/transferbatch"
var FailReasonMsg = map[transferbatch.FailReasonType]string{
"ACCOUNT_FROZEN": "该用户账户被冻结",
"REAL_NAME_CHECK_FAIL": "收款人未实名认证,需要用户完成微信实名认证",
"NAME_NOT_CORRECT": "收款人姓名校验不通过,请核实信息",
"OPENID_INVALID": "Openid格式错误或者不属于商家公众账号",
"TRANSFER_QUOTA_EXCEED": "超过用户单笔收款额度,核实产品设置是否准确",
"DAY_RECEIVED_QUOTA_EXCEED": "超过用户单日收款额度,核实产品设置是否准确",
"MONTH_RECEIVED_QUOTA_EXCEED": "超过用户单月收款额度,核实产品设置是否准确",
"DAY_RECEIVED_COUNT_EXCEED": "超过用户单日收款次数,核实产品设置是否准确",
"PRODUCT_AUTH_CHECK_FAIL": "未开通该权限或权限被冻结,请核实产品权限状态",
"OVERDUE_CLOSE": "超过系统重试期,系统自动关闭",
"ID_CARD_NOT_CORRECT": "收款人身份证校验不通过,请核实信息",
"ACCOUNT_NOT_EXIST": "该用户账户不存在",
"TRANSFER_RISK": "该笔转账可能存在风险,已被微信拦截",
"OTHER_FAIL_REASON_TYPE": "其它失败原因",
"REALNAME_ACCOUNT_RECEIVED_QUOTA_EXCEED": "用户账户收款受限,请引导用户在微信支付查看详情",
"RECEIVE_ACCOUNT_NOT_PERMMIT": "未配置该用户为转账收款人,请在产品设置中调整,添加该用户为收款人",
"PAYEE_ACCOUNT_ABNORMAL": "用户账户收款异常,请联系用户完善其在微信支付的身份信息以继续收款",
"PAYER_ACCOUNT_ABNORMAL": "商户账户付款受限,可前往商户平台获取解除功能限制指引",
"TRANSFER_SCENE_UNAVAILABLE": "该转账场景暂不可用请确认转账场景ID是否正确",
"TRANSFER_SCENE_INVALID": "你尚未获取该转账场景请确认转账场景ID是否正确",
"TRANSFER_REMARK_SET_FAIL": "转账备注设置失败,请调整后重新再试",
"RECEIVE_ACCOUNT_NOT_CONFIGURE": "请前往商户平台-商家转账到零钱-前往功能-转账场景中添加",
"BLOCK_B2C_USERLIMITAMOUNT_BSRULE_MONTH": "超出用户单月转账收款20w限额本月不支持继续向该用户付款",
"BLOCK_B2C_USERLIMITAMOUNT_MONTH": "用户账户存在风险收款受限,本月不支持继续向该用户付款",
"MERCHANT_REJECT": "商户员工(转账验密人)已驳回转账",
"MERCHANT_NOT_CONFIRM": "商户员工(转账验密人)超时未验密",
}

View File

@ -1,53 +0,0 @@
package vo
import "gitea.cdlsxd.cn/sdk/plugin/proto"
// QueryStatus
// INIT: 初始态。 系统转账校验中
// WAIT_PAY: 待确认。待商户确认, 符合免密条件时, 系统会自动扭转为转账中
// PROCESSING:转账中。正在处理中,转账结果尚未明确
// SUCCESS:转账成功
// FAIL:转账失败。需要确认失败原因后,再决定是否重新发起对该笔明细单的转账(并非整个转账批次单)
type QueryStatus string
const (
QueryStatusInit = "INIT"
QueryStatusWaitPay = "WAIT_PAY"
QueryStatusProcessing = "PROCESSING"
QueryStatusSuccess = "SUCCESS"
QueryStatusFail = "FAIL"
)
var queryStatusTextMap = map[QueryStatus]string{
QueryStatusInit: "初始态。 系统转账校验中",
QueryStatusWaitPay: "待确认。待商户确认, 符合免密条件时, 系统会自动扭转为转账中",
QueryStatusProcessing: "转账中。正在处理中,转账结果尚未明确",
QueryStatusSuccess: "转账成功",
QueryStatusFail: "转账失败。需要确认失败原因后,再决定是否重新发起对该笔明细单的转账(并非整个转账批次单)",
}
var queryStatusMap = map[QueryStatus]proto.Status{
QueryStatusInit: proto.Status_ING,
QueryStatusWaitPay: proto.Status_ING,
QueryStatusProcessing: proto.Status_ING,
QueryStatusSuccess: proto.Status_SUCCESS,
QueryStatusFail: proto.Status_FAIL,
}
func (o QueryStatus) GetText() string {
msg, ok := queryStatusTextMap[o]
if !ok {
return ""
}
return msg
}
func (o QueryStatus) GetOrderStatus() proto.Status {
if o == "" {
return proto.Status_INVALID
}
if resultStatus, ok := queryStatusMap[o]; ok {
return resultStatus
}
return proto.Status_FAIL
}

View File

@ -1,14 +0,0 @@
package miniprogram
import (
"plugins/wechat_redpack_v2/internal/wechat/srv/redpack"
)
const (
_sendUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendminiprogramhb"
_queryUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo"
)
type MiniProgram struct {
*redpack.RedPack
}

View File

@ -1,210 +0,0 @@
package miniprogram
import (
"encoding/json"
"encoding/xml"
"fmt"
"github.com/go-playground/validator/v10"
)
type Str interface {
Str() (string, error)
}
type Req interface {
Str
Validate() error
ToXML() ([]byte, error)
}
var _ Req = (*SendRequest)(nil)
var _ Str = (*SendResponse)(nil)
var _ Req = (*QueryRequest)(nil)
var _ Str = (*QueryResponse)(nil)
// SendRequest 红包发送请求结构体(兼容 JSON/XML 双格式XML 无 CDATA 包裹)
type SendRequest struct {
XMLName xml.Name `xml:"xml" json:"-"` // XML 根节点为 <xml>JSON 忽略该字段
NonceStr string `xml:"nonce_str" json:"nonce_str" validate:"required"` // 随机字符串(无 CDATA
Sign string `xml:"sign" json:"sign"` // 签名(无 CDATA
MchBillno string `xml:"mch_billno" json:"mch_billno" validate:"required"` // 商户订单号(无 CDATA
MchId string `xml:"mch_id" json:"mch_id" validate:"required"` // 商户号(无 CDATA
Wxappid string `xml:"wxappid" json:"wxappid" validate:"required"` // 小程序 appid无 CDATA
SendName string `xml:"send_name,omitempty" json:"send_name,omitempty" validate:"required"` // 发送者名称(无 CDATA空值忽略
ReOpenid string `xml:"re_openid" json:"re_openid" validate:"required"` // 用户 openid无 CDATA
TotalAmount int32 `xml:"total_amount" json:"total_amount" validate:"required"` // 付款金额(分,数值类型)
TotalNum int32 `xml:"total_num" json:"total_num" validate:"required"` // 发放人数(数值类型)
Wishing string `xml:"wishing" json:"wishing" validate:"required"` // 祝福语(无 CDATA
ActName string `xml:"act_name" json:"act_name" validate:"required"` // 活动名称(无 CDATA
Remark string `xml:"remark" json:"remark" validate:"required"` // 备注(无 CDATA
NotifyWay string `xml:"notify_way,omitempty" json:"notify_way"` // 通知方式(无 CDATA空值忽略
SceneId string `xml:"scene_id,omitempty" json:"scene_id,omitempty"` // 场景 ID无 CDATA空值忽略
}
// SendResponse 红包发送响应结构体(兼容 JSON/XML 双格式XML 无 CDATA 包裹)
type SendResponse struct {
XMLName xml.Name `xml:"xml" json:"-"` // XML 根节点
ReturnCode string `xml:"return_code,omitempty" json:"return_code,omitempty"` // 返回状态码(无 CDATA
ReturnMsg string `xml:"return_msg,omitempty" json:"return_msg,omitempty"` // 返回信息(无 CDATA
// 以下字段在return_code为SUCCESS的时候有返回
ResultCode string `xml:"result_code,omitempty" json:"result_code,omitempty"` // 业务结果(无 CDATA
ErrCode string `xml:"err_code,omitempty" json:"err_code,omitempty"` // 错误码(无 CDATA
ErrCodeDes string `xml:"err_code_des,omitempty" json:"err_code_des,omitempty"` // 错误描述(无 CDATA修正为 string
// 以下字段在return_code和result_code都为SUCCESS的时候有返回
MchBillno string `xml:"mch_billno,omitempty" json:"mch_billno,omitempty"` // 商户订单号(无 CDATA
MchId string `xml:"mch_id,omitempty" json:"mch_id,omitempty"` // 商户号(无 CDATA
Wxappid string `xml:"wxappid,omitempty" json:"wxappid,omitempty"` // 小程序 appid无 CDATA
ReOpenid string `xml:"re_openid,omitempty" json:"re_openid,omitempty"` // 用户 openid无 CDATA
TotalAmount string `xml:"total_amount,omitempty" json:"total_amount,omitempty"` // 付款金额(无 CDATA
SendListid string `xml:"send_listid,omitempty" json:"send_listid,omitempty"` // 红包列表 ID无 CDATA
Package string `xml:"package,omitempty" json:"package,omitempty"` // 小程序跳转参数(无 CDATA
}
func (req *SendRequest) Str() (string, error) {
b, err := json.Marshal(req)
if err != nil {
return "", err
}
return string(b), nil
}
func (req *SendRequest) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
// ToXML 将 SendRequest 序列化为无 CDATA 的 XML 字节流(带 XML 声明)
func (req *SendRequest) ToXML() ([]byte, error) {
// 生成格式化 XML
data, err := xml.MarshalIndent(req, "", " ")
if err != nil {
return nil, fmt.Errorf("xml marshal failed: %w", err)
}
// 拼接 XML 声明(指定 UTF-8 编码)
xmlData := []byte(xml.Header + string(data))
return xmlData, nil
}
func (req *SendResponse) Str() (string, error) {
b, err := json.Marshal(req)
if err != nil {
return "", err
}
return string(b), nil
}
// QueryRequest .
type QueryRequest struct {
XMLName xml.Name `xml:"xml" json:"-"` // XML 根节点为 <xml>JSON 忽略该字段
NonceStr string `xml:"nonce_str" json:"nonce_str" validate:"required"` // 随机字符串(无 CDATA
MchBillno string `xml:"mch_billno" json:"mch_billno" validate:"required"` // 商户订单号(无 CDATA
MchId string `xml:"mch_id" json:"mch_id" validate:"required"` // 商户号(无 CDATA
Appid string `xml:"appid" json:"appid" validate:"required"` // 小程序 appid无 CDATA
BillType string `xml:"bill_type" json:"bill_type" validate:"required"` // 场景 ID无 CDATA空值忽略 MCHT:通过商户订单号获取红包信息。
Sign string `xml:"sign" json:"sign"` // 签名(无 CDATA
}
// QueryResponse .
type QueryResponse struct {
XMLName xml.Name `xml:"xml" json:"-"` // XML 根节点
ReturnCode string `xml:"return_code" json:"return_code"` // 返回状态码(无 CDATA
ReturnMsg string `xml:"return_msg" json:"return_msg"` // 返回信息(无 CDATA
// 以下字段在return_code为SUCCESS的时候有返回
ResultCode string `xml:"result_code,omitempty" json:"result_code,omitempty"` // 业务结果(无 CDATA
ErrCode string `xml:"err_code,omitempty" json:"err_code,omitempty"` // 错误码(无 CDATA
ErrCodeDes string `xml:"err_code_des,omitempty" json:"err_code_des,omitempty"` // 错误描述(无 CDATA修正为 string
// 以下字段在return_code 和result_code都为SUCCESS的时候有返回
MchBillno string `xml:"mch_billno,omitempty" json:"mch_billno,omitempty"` // 商户订单号(无 CDATA
MchId string `xml:"mch_id,omitempty" json:"mch_id,omitempty"` // 商户号(无 CDATA
DetailId string `xml:"detail_id,omitempty" json:"detail_id,omitempty"` // 使用API发放现金红包时返回的红包单号
Status string `xml:"status,omitempty" json:"status,omitempty"` // 红包状态
SendType string `xml:"send_type,omitempty" json:"send_type,omitempty"` // 发放类型
TotalNum int `xml:"total_num,omitempty" json:"total_num,omitempty"` // 红包个数
TotalAmount int `xml:"total_amount,omitempty" json:"total_amount,omitempty"` // 小程序跳转参数(无 CDATA
Reason string `xml:"reason,omitempty" json:"reason,omitempty"` // 失败原因
SendTime string `xml:"send_time,omitempty" json:"send_time,omitempty"` // 红包发送时间
RefundTime string `xml:"refund_time,omitempty" json:"refund_time,omitempty"` // 红包退款时间
RefundAmount string `xml:"refund_amount,omitempty" json:"refund_amount,omitempty"` // 红包退款金额
Wishing string `xml:"wishing,omitempty" json:"wishing,omitempty"` // 祝福语
Remark string `xml:"remark,omitempty" json:"remark,omitempty"` // 活动描述,低版本微信可见
ActName string `xml:"act_name,omitempty" json:"act_name,omitempty"` // 活动名称
Hblist []Hbinfo `xml:"hblist,omitempty" json:"hblist,omitempty"` // 裂变红包领取列表
Openid string `xml:"openid,omitempty" json:"openid,omitempty"` // 领取红包的Openid
Amount int `xml:"amount,omitempty" json:"amount,omitempty"` // 金额
RcvTime string `xml:"rcv_time,omitempty" json:"rcv_time,omitempty"` // 接收时间
}
type Hbinfo struct {
Openid string `xml:"openid" json:"openid"`
Amount int `xml:"amount" json:"amount"`
RcvTime string `xml:"rcv_time" json:"rcv_time"`
}
func (req *QueryRequest) Str() (string, error) {
b, err := json.Marshal(req)
if err != nil {
return "", err
}
return string(b), nil
}
func (req *QueryRequest) Validate() error {
err := validator.New().Struct(req)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
// ToXML 将 SendRequest 序列化为无 CDATA 的 XML 字节流(带 XML 声明)
func (req *QueryRequest) ToXML() ([]byte, error) {
// 生成格式化 XML
data, err := xml.MarshalIndent(req, "", " ")
if err != nil {
return nil, fmt.Errorf("xml marshal failed: %w", err)
}
// 拼接 XML 声明(指定 UTF-8 编码)
xmlData := []byte(xml.Header + string(data))
return xmlData, nil
}
func (req *QueryResponse) Str() (string, error) {
b, err := json.Marshal(req)
if err != nil {
return "", err
}
return string(b), nil
}
type SendBizRedPacket struct {
TimeStamp string `json:"timeStamp" validate:"required"` // 调用方生成的时间戳,需为字符串
NonceStr string `json:"nonceStr" validate:"required"` // 调用方生成的随机字符串
Package string `json:"package" validate:"required"` // 商户将红包信息组成该串具体方案参见package的说明package需要进行urlencode再传给页面
SignType string `json:"signType" validate:"required"` // 按照文档中所示填入目前仅支持MD5
PaySign string `json:"paySign" validate:"required"` // 签名
}
func (req *SendBizRedPacket) Str() (string, error) {
b, err := json.Marshal(req)
if err != nil {
return "", err
}
return string(b), nil
}

View File

@ -1,77 +0,0 @@
package miniprogram
import (
"context"
"encoding/xml"
"fmt"
)
// Query 查询红包记录
// @link https://pay.weixin.qq.com/doc/v2/merchant/4011937431
func (srv *MiniProgram) Query(ctx context.Context, req *QueryRequest) (response *SendResponse, respBody []byte, err error) {
reqBody, err := srv.queryReqBody(req)
if err != nil {
return
}
respBody, err = srv.Post(ctx, _queryUrl, reqBody)
if err != nil {
return nil, nil, err
}
response, err = srv.ParseXMLQueryResponse(respBody)
return
}
func (srv *MiniProgram) queryReqBody(req *QueryRequest) (reqBody []byte, err error) {
req.BillType = "MCHT"
if err = req.Validate(); err != nil {
return
}
req.Sign = srv.generateQueryReqBodySign(req)
reqBody, err = req.ToXML()
if err != nil {
return
}
return
}
func (srv *MiniProgram) generateQueryReqBodySign(req *QueryRequest) string {
params := make(map[string]string)
if req.NonceStr != "" {
params["nonce_str"] = req.NonceStr
}
if req.MchBillno != "" {
params["mch_billno"] = req.MchBillno
}
if req.MchId != "" {
params["mch_id"] = req.MchId
}
if req.Appid != "" {
params["appid"] = req.Appid
}
if req.BillType != "" {
params["bill_type"] = req.BillType
}
return srv.Sign(params)
}
func (srv *MiniProgram) ParseXMLQueryResponse(xmlData []byte) (*SendResponse, error) {
var v *SendResponse
if err := xml.Unmarshal(xmlData, &v); err != nil {
return nil, fmt.Errorf("xml unmarshal failed: %w", err)
}
return v, nil
}

View File

@ -1,52 +0,0 @@
package miniprogram
import (
"context"
"plugins/wechat_redpack_v2/internal/wechat/srv/redpack"
"testing"
)
func TestRedPack_Query(t *testing.T) {
type args struct {
ctx context.Context
req *QueryRequest
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "小程序查询红包",
args: args{
ctx: context.Background(),
req: &QueryRequest{
NonceStr: "123456",
MchBillno: "1652322442TEST001",
MchId: "1652322442",
Appid: "wx075c784fe71d4d04",
BillType: "",
Sign: "",
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, err := redpack.NewRedPack(cf)
if err != nil {
panic(err)
}
srv := MiniProgram{b}
gotResponse, bodyBytes, err := srv.Query(tt.args.ctx, tt.args.req)
if err != nil {
t.Errorf("Send() error = %v", err)
return
}
t.Errorf("Send() gotResponse = %v, bodyBytes = %s", gotResponse, string(bodyBytes))
})
}
}

View File

@ -1,103 +0,0 @@
package miniprogram
import (
"context"
"encoding/xml"
"fmt"
"strconv"
)
// Send 发送红包
// @link https://pay.weixin.qq.com/doc/v2/merchant/4011937425
func (srv *MiniProgram) Send(ctx context.Context, req *SendRequest) (response *SendResponse, respBody []byte, err error) {
reqBody, err := srv.sendReqBody(req)
if err != nil {
return
}
respBody, err = srv.Post(ctx, _sendUrl, reqBody)
if err != nil {
return
}
response, err = srv.ParseXMLSendResponse(respBody)
if err != nil {
return
}
return
}
func (srv *MiniProgram) sendReqBody(req *SendRequest) (reqBody []byte, err error) {
if err = req.Validate(); err != nil {
return
}
req.Sign = srv.generateSendReqBodySign(req)
reqBody, err = req.ToXML()
if err != nil {
return
}
return
}
func (srv *MiniProgram) generateSendReqBodySign(req *SendRequest) string {
// 1. 构造参数字典
params := make(map[string]string)
if req.NonceStr != "" {
params["nonce_str"] = req.NonceStr
}
if req.MchBillno != "" {
params["mch_billno"] = req.MchBillno
}
if req.MchId != "" {
params["mch_id"] = req.MchId
}
if req.Wxappid != "" {
params["wxappid"] = req.Wxappid
}
if req.SendName != "" {
params["send_name"] = req.SendName
}
if req.ReOpenid != "" {
params["re_openid"] = req.ReOpenid
}
if req.TotalAmount != 0 {
params["total_amount"] = strconv.FormatInt(int64(req.TotalAmount), 10)
}
if req.TotalNum != 0 {
params["total_num"] = strconv.FormatInt(int64(req.TotalNum), 10)
}
if req.Wishing != "" {
params["wishing"] = req.Wishing
}
if req.ActName != "" {
params["act_name"] = req.ActName
}
if req.Remark != "" {
params["remark"] = req.Remark
}
if req.NotifyWay != "" {
params["notify_way"] = req.NotifyWay
}
if req.SceneId != "" {
params["scene_id"] = req.SceneId
}
return srv.Sign(params)
}
func (srv *MiniProgram) ParseXMLSendResponse(xmlData []byte) (*SendResponse, error) {
var v *SendResponse
if err := xml.Unmarshal(xmlData, &v); err != nil {
return nil, fmt.Errorf("xml unmarshal failed: %w", err)
}
return v, nil
}

Some files were not shown because too many files have changed in this diff Show More