Compare commits
No commits in common. "main" and "custom-error" have entirely different histories.
main
...
custom-err
133
Makefile
133
Makefile
|
|
@ -1,52 +1,3 @@
|
|||
# 强制指定 Go 版本(需与主程序编译环境一致)
|
||||
REQUIRED_GO_VERSION := 1.23.6
|
||||
# 目标平台配置(根据运行环境调整,如 arm64)
|
||||
TARGET_GOOS := linux
|
||||
TARGET_GOARCH := amd64
|
||||
# 交叉编译工具链(Mac 编译 Linux amd64 必需)
|
||||
TARGET_CC := x86_64-linux-musl-gcc
|
||||
|
||||
build-linux1:
|
||||
@echo "===== 开始编译 Linux 插件 ====="
|
||||
@echo "要求 Go 版本: $(REQUIRED_GO_VERSION)"
|
||||
@echo "目标平台: $(TARGET_GOOS)/$(TARGET_GOARCH)"
|
||||
|
||||
# 1. 检查 Go 版本
|
||||
@go version | grep -q $(REQUIRED_GO_VERSION) || ( \
|
||||
echo "错误:需要 Go $(REQUIRED_GO_VERSION),当前版本:$(shell go version)"; \
|
||||
exit 1 \
|
||||
)
|
||||
|
||||
# 2. 检查插件目录
|
||||
@[ -d "plugins/${name}" ] || ( \
|
||||
echo "错误:插件目录 plugins/${name} 不存在"; \
|
||||
exit 1 \
|
||||
)
|
||||
|
||||
# 3. 核心:所有命令写在同一行,确保环境变量作用域生效
|
||||
# 注意:&& 连接,且环境变量紧跟 cd 后,与 go build 同进程
|
||||
cd plugins/${name} && \
|
||||
CGO_ENABLED=1 \
|
||||
CC=$(TARGET_CC) \
|
||||
GOOS=$(TARGET_GOOS) \
|
||||
GOARCH=$(TARGET_GOARCH) \
|
||||
GOPROXY=https://goproxy.cn,direct \
|
||||
go build -buildmode=plugin -trimpath -o ../../pkg/linux/${name}.so .
|
||||
|
||||
# 4. 验证编译结果
|
||||
@echo "===== 编译完成 ====="
|
||||
@echo "插件路径: pkg/linux/${name}.so"
|
||||
@echo "插件格式检测:"
|
||||
@file pkg/linux/${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 .
|
||||
|
||||
build-mac:
|
||||
cd plugins; \
|
||||
export GOOS=darwin; \
|
||||
|
|
@ -62,79 +13,69 @@ build-win:
|
|||
export GO111MODULE=on; \
|
||||
export CGO_ENABLED=0; \
|
||||
export GOPROXY=https://goproxy.cn,direct; \
|
||||
cd ${name} && go build -o ../../pkg/win/${name}.so .
|
||||
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-linux name=zltx_v1 && \
|
||||
make build-mac name=zltx_v1 && \
|
||||
make build-linux name=zltx_v1 && \
|
||||
make build-win name=zltx_v1
|
||||
|
||||
.PHONY: zltx_card_v1
|
||||
zltx_card_v1:
|
||||
make build-linux name=zltx_card_v1 && \
|
||||
make build-mac name=zltx_card_v1 && \
|
||||
make build-linux name=zltx_card_v1 && \
|
||||
make build-win name=zltx_card_v1
|
||||
|
||||
.PHONY: zltx_v2
|
||||
zltx_v2:
|
||||
make build-linux name=zltx_v2 && \
|
||||
make build-mac name=zltx_v2 && \
|
||||
make build-linux name=zltx_v2 && \
|
||||
make build-win name=zltx_v2
|
||||
|
||||
.PHONY: alipay_cpn
|
||||
alipay_cpn:
|
||||
make build-linux name=alipay_cpn && \
|
||||
make build-mac name=alipay_cpn && \
|
||||
make build-win name=alipay_cpn
|
||||
|
||||
.PHONY: alipay_redpack
|
||||
alipay_redpack:
|
||||
make build-linux name=alipay_redpack && \
|
||||
make build-mac name=alipay_redpack && \
|
||||
make build-win name=alipay_redpack
|
||||
|
||||
.PHONY: wechat_cpn
|
||||
wechat_cpn:
|
||||
make build-linux name=wechat_cpn && \
|
||||
make build-mac name=wechat_cpn && \
|
||||
make build-win name=wechat_cpn
|
||||
|
||||
.PHONY: wechat_redpack
|
||||
wechat_redpack:
|
||||
make build-linux name=wechat_redpack && \
|
||||
make build-mac name=wechat_redpack && \
|
||||
make build-win 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=wechat_redpack_v2
|
||||
|
||||
.PHONY: union_pay_cpn
|
||||
union_pay_cpn:
|
||||
make build-linux name=union_pay_cpn && \
|
||||
make build-mac name=union_pay_cpn && \
|
||||
make build-linux name=union_pay_cpn && \
|
||||
make build-win name=union_pay_cpn
|
||||
|
||||
.PHONY: union_pay_redpack
|
||||
union_pay_redpack:
|
||||
make build-linux name=union_pay_redpack && \
|
||||
make build-mac name=union_pay_redpack && \
|
||||
make build-linux 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 && \
|
||||
make build-win 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 && \
|
||||
make build-win 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: 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
|
||||
|
|
@ -22,14 +22,9 @@ func getAlpayRedConf() []byte {
|
|||
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=",
|
||||
//}
|
||||
// 福建兴旺主体
|
||||
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
|
||||
|
|
@ -52,11 +47,11 @@ func alipayOrderRedPack() {
|
|||
request := &proto.OrderRequest{
|
||||
Config: getAlpayRedConf(),
|
||||
Order: &proto.OrderRequest_Order{
|
||||
OrderNo: "lsxd20250929001",
|
||||
Account: "18666173766",
|
||||
OrderNo: "lsxd202406071545141534",
|
||||
Account: "123",
|
||||
Quantity: 1,
|
||||
Amount: 0.01,
|
||||
Extra: []byte(`{"name":"李子铭"}`),
|
||||
Extra: []byte(`{"name":"李1子铭"}`),
|
||||
},
|
||||
Product: &proto.OrderRequest_Product{
|
||||
ProductNo: "",
|
||||
|
|
@ -69,7 +64,7 @@ func alipayOrderRedPack() {
|
|||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Printf("Order res:%+v\n", result)
|
||||
log.Printf("Order res:%+v", result)
|
||||
}
|
||||
|
||||
func alipayQueryRedPack() {
|
||||
|
|
@ -91,7 +86,7 @@ func alipayQueryRedPack() {
|
|||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Printf("Query res:%+v\n", resQuery)
|
||||
log.Printf("Query res:%+v", resQuery)
|
||||
}
|
||||
|
||||
func alipayNotifyRedPack() {
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
ERROR ts=2025-06-16T17:21:22+08:00 caller=client/order_digit.go:196 service.id=6551dda3098b service.name=market-gatway service.version=v1.0.1-631-gb1b82e6e msg=回调调用插件服务失败:
|
||||
map[body:{"cardCode":"krorwUJwLmlr7pec6pjcOTOdsc4RrkPFOb27YhFGts3ljozBAeGFmAzZKlFK/RFU","merchantId":25943,"outTradeNo":"202506160907157760019451","status":"01"}
|
||||
config:{"app_id":"101","app_key":"95E7EC7D4A394FF8D11788E5E436DE99","base_uri":"https://openapi.1688sup.com","merchant_id":25943,"notify_url":"https://market.86698.cn/v1/order/direct/notify"}
|
||||
err:rpc error: code = Internal desc = 系统错误:rpc error: code = Unavailable desc = connection error: desc = "transport: error while dialing: dial unix /tmp/plugin1274584100: connect: connection refused"
|
||||
headers:{"Accept":["*/*"],"Accept-Encoding":["gzip, deflate, br"],"Authorization":["MD5 appid=101,sign=7EF17E77501F943FF98620D6D7D45140"],"Connection":["close"],"Content-Length":["152"],"Content-Type":["application/json"],"User-Agent":["Apifox/1.0.0 (https://apifox.com)"],"X-Forwarded-For":["117.175.169.61"],"X-Real-Ip":["117.175.169.61"],"X-Remoteaddr":["172.18.0.1"]} tag:zltx_card_v1]
|
||||
14
cmd/main.go
14
cmd/main.go
|
|
@ -2,17 +2,5 @@ package main
|
|||
|
||||
// main 这只是一个演示
|
||||
func main() {
|
||||
//wechatOrderCpn()
|
||||
//wechatQueryCpn()
|
||||
|
||||
//WechatRedPackV2Order()
|
||||
//WechatRedPackV2Query()
|
||||
//WechatRedPackV2Notify()
|
||||
|
||||
//alipayOrderRedPack()
|
||||
//alipayQueryRedPack()
|
||||
|
||||
//zltxQuery()
|
||||
zltxCardQuery()
|
||||
//zltxCardNotify()
|
||||
alipayOrderRedPack()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,8 @@ var wechatCpnConf = &manage.Config{
|
|||
|
||||
func getWechatCpnConf() []byte {
|
||||
c := &wechat.Server{
|
||||
MchID: "1605446142", // 证书所属商户
|
||||
//MchCertificateSerialNumber: "4D081089DEB385316CBDCB55C070287E4920AC76", // old 过期
|
||||
MchCertificateSerialNumber: "46B64A9AF817BCE0425AB2ED003E7FC3C3DC48D9", // 新的
|
||||
MchID: "1605446142", // 证书所属商户
|
||||
MchCertificateSerialNumber: "4D081089DEB385316CBDCB55C070287E4920AC76",
|
||||
}
|
||||
marshal, _ := json.Marshal(c)
|
||||
return marshal
|
||||
|
|
@ -37,12 +36,12 @@ func wechatOrderCpn() {
|
|||
request := &proto.OrderRequest{
|
||||
Config: getWechatCpnConf(),
|
||||
Order: &proto.OrderRequest_Order{
|
||||
OrderNo: "202512051115219880010012", // {status:ING order_no:"202512051115219880010012" trade_no:"141771369565" message:"成功"}
|
||||
Account: "oknbq5mAjfgPiCJV028g-ZFDx1WU",
|
||||
Extra: []byte(`{"app_id":"wx83fd6da8093f55b7","stock_creator_mchid":"1652465541"}`),
|
||||
OrderNo: "202411221528258970010009",
|
||||
Account: "oknbq5tQyff_vCjjRjCJBElhF1og",
|
||||
Extra: []byte(`{"app_id":"wx83fd6da8093f55b7","stock_creator_mchid":"1679625521"}`),
|
||||
},
|
||||
Product: &proto.OrderRequest_Product{
|
||||
ProductNo: "21144469",
|
||||
ProductNo: "19519911",
|
||||
Extra: []byte(`{}`),
|
||||
},
|
||||
}
|
||||
|
|
@ -63,9 +62,9 @@ func wechatQueryCpn() {
|
|||
Config: getWechatCpnConf(),
|
||||
Order: &proto.QueryRequest_Order{
|
||||
OrderNo: "",
|
||||
TradeNo: "141771369565",
|
||||
Account: "oknbq5mAjfgPiCJV028g-ZFDx1WU",
|
||||
Extra: []byte(`{"app_id":"wx83fd6da8093f55b7"}`),
|
||||
TradeNo: "69445765514",
|
||||
Account: "oO3vO5AxRWgTjmMD38FTvnB5Rq6o",
|
||||
Extra: []byte(`{"app_id":"wx9ed74283ad25bca1"}`),
|
||||
},
|
||||
}
|
||||
resQuery, err := instance.Query(context.Background(), alpayRedConf.Tag, queryRequest)
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
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 wechatRedPackConfig = &manage.Config{
|
||||
Cmd: "pkg/mac/wechat_redpack_v2.so",
|
||||
Tag: "wechat_redpack_v2",
|
||||
Version: 1,
|
||||
CookieKey: "wechat_redpack_v2",
|
||||
CookieValue: "wechat_redpack_v2",
|
||||
}
|
||||
|
||||
type Wechat struct {
|
||||
MchID string `json:"mch_id"`
|
||||
MchCertificateSerialNumber string `json:"mch_certificate_serial_number"`
|
||||
WechatPayPublicKeyID string `json:"wechat_pay_public_key_id"`
|
||||
MchApiV3Key string `json:"mch_api_v3_key"`
|
||||
}
|
||||
|
||||
func getWechatRedPackV2Conf() []byte {
|
||||
c := &Wechat{
|
||||
MchID: "1652322442", // 证书所属商户
|
||||
MchCertificateSerialNumber: "2CE0C4F37E960878F354C986E6F1A5342558BABC",
|
||||
WechatPayPublicKeyID: "PUB_KEY_ID_0116523224422025061800192371001800",
|
||||
MchApiV3Key: "7e6eb4a5ebeed3cf61c693586b11d00b",
|
||||
}
|
||||
marshal, _ := json.Marshal(c)
|
||||
return marshal
|
||||
}
|
||||
|
||||
func WechatRedPackV2Order() {
|
||||
|
||||
orderExtra := struct {
|
||||
Appid string `json:"app_id"`
|
||||
NotifyUrl string `json:"notify_url"`
|
||||
}{
|
||||
Appid: "wxe3bd59243545fa8a",
|
||||
NotifyUrl: "https://transfer.86698.cn/notify/v1/wx_1652322442", // 通知url必须为公网可访问的URL,必须为HTTPS,不能携带参数,因此路由设计 “wx_1652322442” 处理
|
||||
}
|
||||
orderExtraBytes, _ := json.Marshal(orderExtra)
|
||||
|
||||
productExtra := struct {
|
||||
BatchName string `json:"batch_name"`
|
||||
BatchRemark string `json:"batch_remark"`
|
||||
}{
|
||||
BatchName: "LSXD测试",
|
||||
BatchRemark: "LSXD测试~",
|
||||
}
|
||||
productExtraBytes, _ := json.Marshal(productExtra)
|
||||
|
||||
err := manage.Add(wechatRedPackConfig)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer manage.Close()
|
||||
|
||||
request := &proto.OrderRequest{
|
||||
Config: getWechatRedPackV2Conf(),
|
||||
Order: &proto.OrderRequest_Order{
|
||||
OrderNo: "20251106001",
|
||||
Account: "ojbqr6HpeWKFy9Sgdx8yCmmeVJiw",
|
||||
Quantity: 1,
|
||||
Amount: 0.1,
|
||||
Extra: orderExtraBytes,
|
||||
},
|
||||
Product: &proto.OrderRequest_Product{
|
||||
ProductNo: "",
|
||||
Price: 0.1,
|
||||
Type: 0,
|
||||
Extra: productExtraBytes,
|
||||
},
|
||||
}
|
||||
res, err := instance.Order(context.Background(), wechatRedPackConfig.Tag, request)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Printf("Order res:%+v", res)
|
||||
}
|
||||
|
||||
func WechatRedPackV2Query() {
|
||||
|
||||
err := manage.Add(wechatRedPackConfig)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer manage.Close()
|
||||
|
||||
request := &proto.QueryRequest{
|
||||
Config: getWechatRedPackV2Conf(),
|
||||
Order: &proto.QueryRequest_Order{
|
||||
OrderNo: "19957295972489175049",
|
||||
TradeNo: "",
|
||||
Account: "",
|
||||
Extra: []byte(``),
|
||||
},
|
||||
}
|
||||
res, err := instance.Query(context.Background(), wechatRedPackConfig.Tag, request)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Printf("Query res:%+v", res)
|
||||
}
|
||||
|
||||
func WechatRedPackV2Notify() {
|
||||
//body := []byte(`{"id":"fd06376a-3e1b-5516-81f8-9b69cf1ba416","create_time":"2025-07-28T16:10:15+08:00","resource_type":"encrypt-resource","event_type":"MCHTRANSFER.BILL.FINISHED","summary":"商家转账单据终态通知","resource":{"original_type":"mch_payment","algorithm":"AEAD_AES_256_GCM","ciphertext":"XJBIhrHgbe9NR5q/jLYmZKdT/3xuKm2x7EFu3T52Hj2hjPzarRSA2HCsGTxGojfD+CFyJHIULlL2adqLijAjpi3B6TaYKY4LqhtJ/RYSQtYNxYvBpWX1yLOWe8luJbWxmQvKZxIekFs8lGVgkPBUw0IfEAvJ6jHAGCcgxLIqxgOf6UtGUqxCCNp/V3xy8zCiHB0Mvlw8eXCTuG+ZESJIXvloVGNS79R6iNeqk4kNKRSaV86MNh1KQlmoBxZ4yEshD/vIlMulU3xEc+mM25y8vUS4Ot6pxEpUdUyjwcb9QTwTTnZzm6i+VWYymcItAVBQrvsKBMmqWnPtNXG8++13k3DeO1LyVKURmnWXXT1mImmGx/teN/1xPV5y6nChu/HTbcJGDQy2twuq6TPFbbYlTjZH047z/ZtozJNvGNeh","associated_data":"mch_payment","nonce":"YN3eW5H8mxLs"}}`)
|
||||
|
||||
// {status:SUCCESS order_no:"199285809595654553611" trade_no:"1330001821912602511240016633315206" message:"转账成功" data:"{\"mch_id\":\"1652322",\"out_bill_no\":\"199285809595654553611\",\"transfer_bill_no\":\"1330001821912602511240016633315206\",\"state\":\"SUCCESS\",\"transfer_amount\":10,\"openid\":\"ojbqr6HpeWKFy9Sgdx8yCmmeVJiw\",\"create_time\":\"2025-11-24T15:29:30+08:00\",\"update_time\":\"2025-11-24T15:29:33+08:00\"}"}
|
||||
body := []byte(`{"id":"257c6ec4-ba77-5ff3-88e9-6f29de429ad3","create_time":"2025-11-24T15:29:33+08:00","resource_type":"encrypt-resource","event_type":"MCHTRANSFER.BILL.FINISHED","summary":"商家转账单据终态通知","resource":{"original_type":"mch_payment","algorithm":"AEAD_AES_256_GCM","ciphertext":"tI+cPf7oYifI3Wzwn7p77H8pHRnlouqvnzVxpBS0IsTh3VyyHD2yyaSOWRaxjHvwMIlnwecfoVn3Zbg9Frt+WF3VYzNUXDcYIGrX17bo+Xs8pw7F2ZLhIXAUxpS2okz8hEAcrfHo4XfmesglsyyJHlYpb4GPnOMkI+fmU8PiYcIIs6ZbUsQRJE3n24CCkl5DDyXQAMQB+drvXiWSCTcVOiBdE1MLTDNcGTrMubRpi6m/fIAm8iZIIa+KwZk3nXV8reFHm3NDf8Wb3EZacfgv1fX1z6Ps57qn47EhcaU7J25sQBcU4snnLZUS7Zisd6VRiQ9OwEcjoSH6U5FrUPz8+cCPwZjfTD66B8y11sBOjKA43NtbzUzWnA56X8n1OrNinBlreLdH2JXUwzvSjR/QzLFKNjDvtcllBdQjql8=","associated_data":"mch_payment","nonce":"92mubmsnu0qU"}}`)
|
||||
//hds := `{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["775"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["dF8R9izUJnPBjVLa2cAcCaa7j6QUgitl"],"Wechatpay-Serial":["PUB_KEY_ID_0116523224422025061800192371001800"],"Wechatpay-Signature":["SoXIiTRTr6jofXXxGlfO+wyf1IzXFXcsfvEU2EggQfRKFu+8h3TT6QMQ8zIf8dpkkTPexB/3igGiATrR3uZY4ZeOpRrhIFHSJj0Ala0Ri2Nt4zk+MuBQnhybSYJ4Cn3/sHC4i2HFoOSil7OqlSr79hjod3h0tjYVQLtZ4+Cjp0IeMNB4p5qmIuERuhtfRqcyqXik9/uYNYxw8/Wkf1mMnTsBxyXK3iHAoinXNrEiqCCrQHCfnORMYosr7l+Ox8v9u1c8FFt+rt09vKssVCqYaZ/XRala3mjslDRiluFKSuqb7/JO3AxQjBK6M0iSZOlnmiXSIAq+UxJg4cem6wHi+g=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1753690220"],"X-Forwarded-For":["121.51.58.168"],"X-Forwarded-Proto":["https"],"X-Real-Ip":["121.51.58.168"]}`
|
||||
hds := `{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["775"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["WsHviCJBykU2aWzeuQNy1ncKJHTK0gFR"],"Wechatpay-Serial":["PUB_KEY_ID_0116523224422025061800192371001800"],"Wechatpay-Signature":["EeKLZ0G71ki+xQmjlHW7fGA9o8/zXej41CD4hVBO9AOJSXQZdI5UjeSxmRMc+VlkDVxpF0KqvbIIUN6BKhyatEN/sRfMwwLDAx7IOsnO2DPXAAbJd4p37pSJz48fnQj693SVPn0QWC/kaYeRjag52V1VonpZNMb/SJv77eu/bmvmHYd09qBagJot/8T0uhMcKKsVroLfB4OkabkCrzAnNlVYdBtnjrVnWGIYuZQ44kCweWHLCz6fb8/m2AATgm2bVrfcxYkdbdAcdlogQapByVjWWXZCzOA9V7FpHpGEfrUK2gxw9moXcA6sFkgntKV9XiKZueKozxP2l8jJyIJ+wQ=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1763969378"],"X-Forwarded-For":["121.51.58.173"],"X-Real-Ip":["121.51.58.173"],"X-Remoteaddr":["172.21.0.1"]}`
|
||||
|
||||
if err := manage.Add(wechatRedPackConfig); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer manage.Close()
|
||||
|
||||
request := &proto.NotifyRequest{
|
||||
Config: getWechatRedPackV2Conf(),
|
||||
Queries: nil,
|
||||
Headers: []byte(hds),
|
||||
Body: body,
|
||||
}
|
||||
res, err := instance.Notify(context.Background(), wechatRedPackConfig.Tag, request)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Printf("Query res:%+v", res)
|
||||
}
|
||||
45
cmd/zltx.go
45
cmd/zltx.go
|
|
@ -10,27 +10,25 @@ import (
|
|||
)
|
||||
|
||||
var zltxConf = &manage.Config{
|
||||
Cmd: "pkg/mac/zltx_v1.so",
|
||||
Tag: "zltx_v1",
|
||||
Cmd: "pkg/mac/zltx.so",
|
||||
Tag: "zltx",
|
||||
Version: 1,
|
||||
CookieKey: "zltx_v1",
|
||||
CookieValue: "zltx_v1",
|
||||
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"`
|
||||
MerchantId int64 `json:"merchant_id"`
|
||||
AppId string `json:"app_id"`
|
||||
AppKey string `json:"app_key"`
|
||||
BaseUri string `json:"base_uri"`
|
||||
NotifyUrl string `json:"notify_url"`
|
||||
}
|
||||
c := &Config{
|
||||
AppId: "1",
|
||||
AppKey: "1e2bf7a04b8b1e6be5dc78d04e8639c9",
|
||||
BaseUri: "http://test.openapi.1688sup.cn",
|
||||
NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify",
|
||||
MerchantId: 25537,
|
||||
AppId: "23329",
|
||||
AppKey: "8db16e8cc8363ed4eb4c14f9520bcc32",
|
||||
BaseUri: "http://test.openapi.1688sup.cn",
|
||||
NotifyUrl: "http://test.openapi.1688sup.cn",
|
||||
}
|
||||
marshal, _ := json.Marshal(c)
|
||||
return marshal
|
||||
|
|
@ -74,22 +72,3 @@ func zltx() {
|
|||
}
|
||||
log.Printf("Query res:%+v", resQuery)
|
||||
}
|
||||
|
||||
func zltxQuery() {
|
||||
err := manage.Add(zltxConf)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer manage.Close()
|
||||
queryRequest := &proto.QueryRequest{
|
||||
Config: config(),
|
||||
Order: &proto.QueryRequest_Order{
|
||||
OrderNo: "test_plugin_zltx_v1_direct_2",
|
||||
},
|
||||
}
|
||||
resQuery, err := instance.Query(context.Background(), zltxConf.Tag, queryRequest)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Printf("Query res:%+v", resQuery)
|
||||
}
|
||||
|
|
|
|||
105
cmd/zltx_card.go
105
cmd/zltx_card.go
|
|
@ -1,105 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"gitea.cdlsxd.cn/sdk/plugin/instance"
|
||||
"gitea.cdlsxd.cn/sdk/plugin/manage"
|
||||
"gitea.cdlsxd.cn/sdk/plugin/proto"
|
||||
"log"
|
||||
)
|
||||
|
||||
var zltxCardConf = &manage.Config{
|
||||
Cmd: "pkg/mac/zltx_card_v1.so",
|
||||
Tag: "zltx_card_v1",
|
||||
Version: 1,
|
||||
CookieKey: "zltx_card_v1",
|
||||
CookieValue: "zltx_card_v1",
|
||||
}
|
||||
|
||||
func zlxtCardCf() []byte {
|
||||
|
||||
return []byte(`{"app_id":"101","app_key":"95E7EC7D4A394FF8D11788E5E436DE99","base_uri":"https://openapi.1688sup.com","merchant_id":25943,"notify_url":"https://market.86698.cn/v1/order/direct/notify"}`)
|
||||
|
||||
//type Config struct {
|
||||
// AppId string `json:"app_id"`
|
||||
// AppKey string `json:"app_key"`
|
||||
// BaseUri string `json:"base_uri"`
|
||||
// NotifyUrl string `json:"notify_url"`
|
||||
// MerchantId int64 `json:"merchant_id"`
|
||||
//}
|
||||
//c := &Config{
|
||||
// AppId: "101",
|
||||
// AppKey: "95E7EC7D4A394FF8D11788E5E436DE99",
|
||||
// BaseUri: "https://openapi.1688sup.com",
|
||||
// NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify",
|
||||
// MerchantId: 25943,
|
||||
//}
|
||||
//marshal, _ := json.Marshal(c)
|
||||
//return marshal
|
||||
}
|
||||
|
||||
func zltxCardQuery() {
|
||||
|
||||
err := manage.Add(zltxCardConf)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
fmt.Println("zltx_card_v1_1 close start")
|
||||
manage.Close()
|
||||
fmt.Println("zltx_card_v1_1 close end")
|
||||
}()
|
||||
|
||||
for i := 0; i < 1; i++ {
|
||||
req := &proto.QueryRequest{
|
||||
Config: zlxtCardCf(),
|
||||
Order: &proto.QueryRequest_Order{
|
||||
OrderNo: "test_plugin_zltx_v1_card_3",
|
||||
TradeNo: "",
|
||||
Account: "",
|
||||
Extra: nil,
|
||||
},
|
||||
}
|
||||
res, err := instance.Query(context.Background(), zltxCardConf.Tag, req)
|
||||
if err != nil {
|
||||
log.Printf("query err:%+v i:%d", err, i)
|
||||
} else {
|
||||
log.Printf("query res:%+v i:%d", res, i)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// main 这只是一个演示
|
||||
func zltxCardNotify() {
|
||||
|
||||
err := manage.Add(zltxCardConf)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
fmt.Println("zltx_card_v1_1 close start")
|
||||
manage.Close()
|
||||
fmt.Println("zltx_card_v1_1 close end")
|
||||
}()
|
||||
|
||||
// transport: error while dialing: dial unix /tmp/plugin2702936918: connect: connection refused
|
||||
for i := 0; i < 5; i++ {
|
||||
req := &proto.NotifyRequest{
|
||||
Config: zlxtCardCf(),
|
||||
Queries: []byte(``),
|
||||
Headers: []byte(`{"Accept":["*/*"],"Accept-Encoding":["gzip, deflate, br"],"Authorization":["MD5 appid=101,sign=7EF17E77501F943FF98620D6D7D45140"],"Connection":["close"],"Content-Length":["152"],"Content-Type":["application/json"],"User-Agent":["Apifox/1.0.0 (https://apifox.com)"],"X-Forwarded-For":["117.175.169.61"],"X-Real-Ip":["117.175.169.61"],"X-Remoteaddr":["172.18.0.1"]}`),
|
||||
Body: []byte(`{"cardCode":"krorwUJwLmlr7pec6pjcOTOdsc4RrkPFOb27YhFGts3ljozBAeGFmAzZKlFK/RFU","merchantId":25943,"outTradeNo":"202506160907157760019451","status":"01"}`),
|
||||
}
|
||||
res, err := instance.Notify(context.Background(), zltxCardConf.Tag, req)
|
||||
if err != nil {
|
||||
log.Printf("notify err:%+v i:%d", err, i)
|
||||
} else {
|
||||
log.Printf("notify res:%+v i:%d", res, i)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
3
go.mod
3
go.mod
|
|
@ -3,7 +3,7 @@ module plugins
|
|||
go 1.22.2
|
||||
|
||||
require (
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17
|
||||
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca
|
||||
plugins/utils v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
|
||||
|
|
@ -12,7 +12,6 @@ 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
|
||||
|
|
|
|||
6
go.sum
6
go.sum
|
|
@ -1,5 +1,5 @@
|
|||
gitea.cdlsxd.cn/sdk/plugin v1.0.17 h1:agk+9iA1ZI6fLVLtxEnuOWxcDzSq9QH7VBFvhlZZsbw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
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=
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ go 1.22.2
|
|||
replace plugins/utils => ../../utils
|
||||
|
||||
require (
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19
|
||||
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.9.0
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
gitea.cdlsxd.cn/sdk/plugin v1.0.17 h1:agk+9iA1ZI6fLVLtxEnuOWxcDzSq9QH7VBFvhlZZsbw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19 h1:j0Ifn3q+C7ibxSTfL1KbmnX1k/VO9e0XMDJSuPutixU=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
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=
|
||||
|
|
@ -32,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=
|
||||
|
|
@ -49,8 +45,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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
|
|
@ -70,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=
|
||||
|
|
|
|||
|
|
@ -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 = fmt.Errorf("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 = fmt.Errorf("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 = fmt.Errorf("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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,14 +12,13 @@ import (
|
|||
var server = &AlipayCpnService{}
|
||||
|
||||
func config() []byte {
|
||||
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"}`)
|
||||
//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) {
|
||||
|
|
@ -31,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(`{}`),
|
||||
},
|
||||
}
|
||||
|
|
@ -59,11 +57,10 @@ func TestQuery(t *testing.T) {
|
|||
request := &proto.QueryRequest{
|
||||
Config: config(),
|
||||
Order: &proto.QueryRequest_Order{
|
||||
OrderNo: "202507291411243510013587",
|
||||
OrderNo: "240403180614988314_80",
|
||||
TradeNo: "",
|
||||
Account: "18043057911",
|
||||
//Extra: []byte(`{"phone_id":"","logon_id":"13691105465","activity_id":"ACT873CCV02108400"}`),
|
||||
Extra: []byte(`{"phone_id":"","logon_id":"18043057911","activity_id":"ACT673CCV03194025"}`),
|
||||
Account: "",
|
||||
Extra: []byte(`{"phone_id":"","logon_id":"13691105465","activity_id":"ACT873CCV02108400"}`),
|
||||
},
|
||||
}
|
||||
t.Run("TestQuery", func(t *testing.T) {
|
||||
|
|
@ -95,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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ go 1.22.2
|
|||
replace plugins/utils => ../../utils
|
||||
|
||||
require (
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19
|
||||
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.9.0
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
gitea.cdlsxd.cn/sdk/plugin v1.0.17 h1:agk+9iA1ZI6fLVLtxEnuOWxcDzSq9QH7VBFvhlZZsbw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19 h1:j0Ifn3q+C7ibxSTfL1KbmnX1k/VO9e0XMDJSuPutixU=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
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=
|
||||
|
|
@ -32,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=
|
||||
|
|
@ -52,8 +48,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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
|
|
@ -75,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=
|
||||
|
|
|
|||
|
|
@ -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 = fmt.Errorf("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), nil
|
||||
}
|
||||
|
||||
func (s *AlipayRedPackService) 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)
|
||||
}
|
||||
}()
|
||||
|
||||
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 = fmt.Errorf("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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -123,13 +122,13 @@ func orderResp(request *proto.OrderRequest, resp *po.OrderResp) *proto.OrderResp
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
@ -152,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,
|
||||
|
|
@ -165,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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.19
|
||||
github.com/go-playground/validator/v10 v10.22.0
|
||||
github.com/hashicorp/go-plugin v1.6.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
plugins/utils v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fatih/color v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-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
|
||||
)
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
gitea.cdlsxd.cn/sdk/plugin v1.0.17 h1:agk+9iA1ZI6fLVLtxEnuOWxcDzSq9QH7VBFvhlZZsbw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19 h1:j0Ifn3q+C7ibxSTfL1KbmnX1k/VO9e0XMDJSuPutixU=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
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.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=
|
||||
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=
|
||||
|
|
@ -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"`
|
||||
}
|
||||
|
|
@ -1,69 +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 `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) GetOrderStatus() proto.Status {
|
||||
if o.IsSuccess() {
|
||||
return proto.Status_ING
|
||||
}
|
||||
return proto.Status_FAIL
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
||||
}
|
||||
|
|
@ -1,48 +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))
|
||||
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()
|
||||
}
|
||||
|
|
@ -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), nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
|
@ -1,149 +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 {
|
||||
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))
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
@ -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.19
|
||||
github.com/go-playground/validator/v10 v10.22.0
|
||||
github.com/hashicorp/go-plugin v1.6.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
plugins/utils v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fatih/color v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-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
|
||||
)
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
gitea.cdlsxd.cn/sdk/plugin v1.0.17 h1:agk+9iA1ZI6fLVLtxEnuOWxcDzSq9QH7VBFvhlZZsbw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19 h1:j0Ifn3q+C7ibxSTfL1KbmnX1k/VO9e0XMDJSuPutixU=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
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.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=
|
||||
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=
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,53 +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
|
||||
}
|
||||
return fmt.Sprintf("code:[%v],msg:[%s]", o.Code, o.Msg)
|
||||
}
|
||||
|
||||
func (o *OrderResp) GetOrderStatus() proto.Status {
|
||||
|
||||
if o.IsSuccess() {
|
||||
return proto.Status_ING
|
||||
}
|
||||
return proto.Status_FAIL
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -1,148 +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"
|
||||
)
|
||||
|
||||
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 = 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), nil
|
||||
}
|
||||
|
||||
func (s *QiXingWechatRedPackService) 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 *QiXingWechatRedPackService) 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 *QiXingWechatRedPackService) Notify(_ context.Context, request *proto.NotifyRequest) (resp2 *proto.NotifyResponse, respErr error) {
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
respErr = fmt.Errorf("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)
|
||||
}
|
||||
|
|
@ -1,131 +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",
|
||||
BasUrl: "https://cdc.myviding.com/cdc-api/api/external/red-packet",
|
||||
NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/supplier/notify/51",
|
||||
Official: Official{
|
||||
Name: "福建兴旺网络科技有限公司",
|
||||
AppId: "wxe3bd59243545fa8a",
|
||||
AppSecret: "4c9649cb998f71038e187b4c58f5fda0",
|
||||
OauthKey: "bcee0c6753b2a31c792a91fe9f9f1666",
|
||||
OauthUrl: "https://utils.85938.cn/utils/v1/wechat/oauth/fjxw",
|
||||
},
|
||||
}
|
||||
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: "lsxd20250929004",
|
||||
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: "lsxd20250929003",
|
||||
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))
|
||||
})
|
||||
}
|
||||
|
|
@ -1,168 +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 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"` // 公众号信息
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ go 1.22.2
|
|||
replace plugins/utils => ../../utils
|
||||
|
||||
require (
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19
|
||||
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
|
||||
|
|
@ -17,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
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17 h1:agk+9iA1ZI6fLVLtxEnuOWxcDzSq9QH7VBFvhlZZsbw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19 h1:j0Ifn3q+C7ibxSTfL1KbmnX1k/VO9e0XMDJSuPutixU=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
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=
|
||||
|
|
@ -20,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=
|
||||
|
|
@ -57,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=
|
||||
|
|
@ -75,8 +69,6 @@ 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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
|
|
@ -141,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=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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("验签失败")
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ go 1.22.2
|
|||
replace plugins/utils => ../../utils
|
||||
|
||||
require (
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19
|
||||
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
|
||||
|
|
@ -17,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
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17 h1:agk+9iA1ZI6fLVLtxEnuOWxcDzSq9QH7VBFvhlZZsbw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19 h1:j0Ifn3q+C7ibxSTfL1KbmnX1k/VO9e0XMDJSuPutixU=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.19/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
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=
|
||||
|
|
@ -20,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=
|
||||
|
|
@ -57,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=
|
||||
|
|
@ -75,8 +69,6 @@ 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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
|
|
@ -141,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=
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ go 1.22.2
|
|||
replace plugins/utils => ../../utils
|
||||
|
||||
require (
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17
|
||||
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca
|
||||
github.com/hashicorp/go-plugin v1.6.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.18
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
gitea.cdlsxd.cn/sdk/plugin v1.0.17 h1:agk+9iA1ZI6fLVLtxEnuOWxcDzSq9QH7VBFvhlZZsbw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
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=
|
||||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ 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"
|
||||
)
|
||||
|
|
@ -14,7 +15,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
|
||||
|
|
@ -88,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{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
)
|
||||
|
||||
// 插件通信信息,若不对应则会报错panic
|
||||
// 微信立减金文档地址: https://pay.weixin.qq.com/doc/v3/merchant/4012463767
|
||||
const (
|
||||
Tag = "wechat_cpn"
|
||||
Version = 1
|
||||
|
|
@ -19,109 +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 = fmt.Errorf("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.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 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 = fmt.Errorf("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 = fmt.Errorf("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])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ go 1.22.2
|
|||
replace plugins/utils => ../../utils
|
||||
|
||||
require (
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17
|
||||
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca
|
||||
github.com/hashicorp/go-plugin v1.6.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.18
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
gitea.cdlsxd.cn/sdk/plugin v1.0.17 h1:agk+9iA1ZI6fLVLtxEnuOWxcDzSq9QH7VBFvhlZZsbw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
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=
|
||||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = fmt.Errorf("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(ctx, 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 = fmt.Errorf("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(ctx, 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
module plugins/wechat_redpack_v2
|
||||
|
||||
go 1.22.2
|
||||
|
||||
replace plugins/utils => ../../utils
|
||||
|
||||
require (
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17
|
||||
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
|
||||
)
|
||||
|
||||
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/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/stretchr/testify v1.9.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
|
||||
)
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
gitea.cdlsxd.cn/sdk/plugin v1.0.17 h1:agk+9iA1ZI6fLVLtxEnuOWxcDzSq9QH7VBFvhlZZsbw=
|
||||
gitea.cdlsxd.cn/sdk/plugin v1.0.17/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
|
||||
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=
|
||||
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/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/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/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=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/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/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/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=
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package vo
|
||||
|
||||
type Code int
|
||||
|
||||
const CodeSuccess Code = 200
|
||||
|
||||
func (c Code) Value() int {
|
||||
return int(c)
|
||||
}
|
||||
|
|
@ -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": "商户员工(转账验密人)超时未验密",
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package miniprogram
|
||||
|
||||
import "context"
|
||||
|
||||
func (srv *MiniProgram) SendBizRedPacketSign(ctx context.Context, req *SendBizRedPacket) {
|
||||
|
||||
req.PaySign = srv.generateSendBizRedPacketSignSign(req)
|
||||
return
|
||||
}
|
||||
|
||||
func (srv *MiniProgram) generateSendBizRedPacketSignSign(req *SendBizRedPacket) string {
|
||||
|
||||
params := make(map[string]string)
|
||||
|
||||
if req.TimeStamp != "" {
|
||||
params["timeStamp"] = req.TimeStamp
|
||||
}
|
||||
if req.NonceStr != "" {
|
||||
params["nonce_str"] = req.NonceStr
|
||||
}
|
||||
if req.Package != "" {
|
||||
params["package"] = req.Package
|
||||
}
|
||||
params["appid"] = "MD5"
|
||||
|
||||
return srv.Sign(params)
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
package miniprogram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"plugins/wechat_redpack_v2/internal/wechat/srv/redpack"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var cf = &redpack.Config{
|
||||
MchId: "1652322442",
|
||||
ApiV2Key: "bcee0c6753b2a31c792a91fe9f9f1666",
|
||||
CertFile: "/Users/lsxd/code/go/lsxd/market/plugins//cert/wechat/1652322442_2/apiclient_cert.pem",
|
||||
KeyFile: "/Users/lsxd/code/go/lsxd/market/plugins//cert/wechat/1652322442_2/apiclient_key.pem",
|
||||
}
|
||||
|
||||
func TestRedPack_Send(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *SendRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "小程序发红包",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
req: &SendRequest{
|
||||
NonceStr: "wxe3bd59243545fa8a",
|
||||
Sign: "",
|
||||
MchBillno: "1652322442TEST001",
|
||||
MchId: "1652322442",
|
||||
Wxappid: "wx075c784fe71d4d04",
|
||||
SendName: "福建兴旺",
|
||||
ReOpenid: "odX1x1za5bIooAz7gG9Jx3lHSHxk",
|
||||
TotalAmount: 10,
|
||||
TotalNum: 1,
|
||||
Wishing: "测试红包",
|
||||
ActName: "测试活动红包",
|
||||
Remark: "测试红包备注",
|
||||
NotifyWay: "MINI_PROGRAM_JSAPI",
|
||||
SceneId: "",
|
||||
},
|
||||
},
|
||||
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.Send(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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRedPack_SendBizRedPacketSign(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *SendBizRedPacket
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "小程序发红包",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
req: &SendBizRedPacket{
|
||||
TimeStamp: fmt.Sprintf("%d", time.Now().Unix()),
|
||||
NonceStr: "wxe3bd59243545fa8a11",
|
||||
Package: "sendid%3D242e8abd163d300019b2cae74ba8e8c06e3f0e51ab84d16b3c80decd22a5b672%26ver%3D8%26sign%3D4110d649a5aef52dd6b95654ddf91ca7d5411ac159ace4e1a766b7d3967a1c3dfe1d256811445a4abda2d9cfa4a9b377a829258bd00d90313c6c346f2349fe5d%26mchid%3D11475856%26appid%3Dwxd27ebc41b85ce36d",
|
||||
SignType: "MD5",
|
||||
PaySign: "",
|
||||
},
|
||||
},
|
||||
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}
|
||||
|
||||
srv.SendBizRedPacketSign(tt.args.ctx, tt.args.req)
|
||||
|
||||
str, _ := tt.args.req.Str()
|
||||
t.Logf("SendBizRedPacketSign() gotResponse = %s", str)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package official
|
||||
|
||||
import (
|
||||
"plugins/wechat_redpack_v2/internal/wechat/srv/redpack"
|
||||
)
|
||||
|
||||
const (
|
||||
_sendUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack"
|
||||
_queryUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo"
|
||||
)
|
||||
|
||||
type Official struct {
|
||||
*redpack.RedPack
|
||||
}
|
||||
|
|
@ -1,192 +0,0 @@
|
|||
package official
|
||||
|
||||
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)
|
||||
ClientIp string `xml:"client_ip" json:"client_ip" validate:"required"` // 该IP同在商户平台设置的IP白名单中的IP没有关联,该IP可传用户端或者服务端的IP。
|
||||
ActName string `xml:"act_name" json:"act_name" validate:"required"` // 活动名称(无 CDATA)
|
||||
Remark string `xml:"remark" json:"remark" validate:"required"` // 备注(无 CDATA)
|
||||
SceneId string `xml:"scene_id,omitempty" json:"scene_id,omitempty"` // 场景 ID(无 CDATA,空值忽略)
|
||||
RiskInfo string `xml:"risk_info,omitempty" json:"risk_info,omitempty"` // 活动信息
|
||||
}
|
||||
|
||||
// SendResponse 红包发送响应结构体(兼容 JSON/XML 双格式,XML 无 CDATA 包裹)
|
||||
type SendResponse struct {
|
||||
XMLName xml.Name `xml:"xml" json:"-"` // XML 根节点
|
||||
ReturnCode string `xml:"return_code,omitempty" json:"return_code"` // 返回状态码(无 CDATA)
|
||||
ReturnMsg string `xml:"return_msg,omitempty" 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)
|
||||
Wxappid string `xml:"wxappid,omitempty" json:"wxappid,omitempty"` // 小程序 appid(无 CDATA)
|
||||
ReOpenid string `xml:"re_openid,omitempty" json:"re_openid,omitempty"` // 用户 openid(无 CDATA)
|
||||
SendListid string `xml:"send_listid,omitempty" json:"send_listid,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
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
package official
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Query 查询红包记录
|
||||
// @link https://pay.weixin.qq.com/doc/v2/merchant/4011981612
|
||||
func (srv *Official) 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 *Official) 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 *Official) 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 *Official) 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
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
package official
|
||||
|
||||
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(&redpack.Config{
|
||||
MchId: "1652322442",
|
||||
ApiV2Key: "bcee0c6753b2a31c792a91fe9f9f1666",
|
||||
CertFile: "/Users/lsxd/code/go/lsxd/market/plugins//cert/wechat/1652322442_2/apiclient_cert.pem",
|
||||
KeyFile: "/Users/lsxd/code/go/lsxd/market/plugins//cert/wechat/1652322442_2/apiclient_key.pem",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
srv := Official{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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
package official
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Send 发送红包
|
||||
// @link https://pay.weixin.qq.com/doc/v2/merchant/4011974053
|
||||
func (srv *Official) 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 *Official) 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 *Official) 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.ClientIp != "" {
|
||||
params["client_ip"] = req.ClientIp
|
||||
}
|
||||
if req.SceneId != "" {
|
||||
params["scene_id"] = req.SceneId
|
||||
}
|
||||
if req.RiskInfo != "" {
|
||||
params["risk_info"] = req.RiskInfo
|
||||
}
|
||||
|
||||
return srv.Sign(params)
|
||||
}
|
||||
|
||||
func (srv *Official) 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
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
package official
|
||||
|
||||
import (
|
||||
"context"
|
||||
"plugins/wechat_redpack_v2/internal/wechat/srv/redpack"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRedPack_Send(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *SendRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "小程序发红包",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
req: &SendRequest{
|
||||
NonceStr: "wxe3bd59243545fa8a",
|
||||
Sign: "",
|
||||
MchBillno: "1652322442TEST001",
|
||||
MchId: "1652322442",
|
||||
Wxappid: "wxe3bd59243545fa8a",
|
||||
SendName: "福建兴旺",
|
||||
ReOpenid: "ojbqr6HpeWKFy9Sgdx8yCmmeVJiw",
|
||||
TotalAmount: 10,
|
||||
TotalNum: 1,
|
||||
ClientIp: "127.0.0.1",
|
||||
Wishing: "测试公众号红包",
|
||||
ActName: "测试公众号活动红包",
|
||||
Remark: "测试公众号红包备注",
|
||||
SceneId: "",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
b, err := redpack.NewRedPack(&redpack.Config{
|
||||
MchId: "1652322442",
|
||||
ApiV2Key: "bcee0c6753b2a31c792a91fe9f9f1666",
|
||||
CertFile: "/Users/lsxd/code/go/lsxd/market/plugins//cert/wechat/1652322442_2/apiclient_cert.pem",
|
||||
KeyFile: "/Users/lsxd/code/go/lsxd/market/plugins//cert/wechat/1652322442_2/apiclient_key.pem",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
srv := Official{b}
|
||||
|
||||
gotResponse, bodyBytes, err := srv.Send(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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
package redpack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
MchId string
|
||||
ApiV2Key string
|
||||
CertFile string // 证书路径
|
||||
KeyFile string // 私钥路径
|
||||
}
|
||||
|
||||
type RedPack struct {
|
||||
Config *Config
|
||||
|
||||
transport *http.Transport
|
||||
}
|
||||
|
||||
func NewRedPack(config *Config) (*RedPack, error) {
|
||||
|
||||
// 加载证书,配置双向 TLS
|
||||
cert, err := tls.LoadX509KeyPair(config.CertFile, config.KeyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load cert failed: %w", err)
|
||||
}
|
||||
|
||||
// 关键修改:不创建新的 certPool,而是使用系统默认根证书池
|
||||
// 这样客户端能验证微信支付服务端的证书合法性
|
||||
tlsConfig := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert}, // 仅添加商户证书(用于双向认证)
|
||||
// 注释掉 RootCAs 配置,让 Go 使用系统默认根证书池
|
||||
// RootCAs: clientCertPool,
|
||||
}
|
||||
transport := &http.Transport{TLSClientConfig: tlsConfig}
|
||||
|
||||
return &RedPack{Config: config, transport: transport}, nil
|
||||
}
|
||||
|
||||
func (srv *RedPack) Sign(params map[string]string) string {
|
||||
|
||||
// 排序
|
||||
var keys []string
|
||||
for k := range params {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
// 拼接字符串
|
||||
var buf strings.Builder
|
||||
for _, k := range keys {
|
||||
buf.WriteString(k)
|
||||
buf.WriteString("=")
|
||||
buf.WriteString(params[k])
|
||||
buf.WriteString("&")
|
||||
}
|
||||
// 拼接 API 密钥
|
||||
buf.WriteString("key=")
|
||||
buf.WriteString(srv.Config.ApiV2Key)
|
||||
|
||||
hash := md5.Sum([]byte(buf.String()))
|
||||
sign := strings.ToUpper(hex.EncodeToString(hash[:]))
|
||||
return sign
|
||||
}
|
||||
|
||||
func (srv *RedPack) Post(_ context.Context, url string, reqBody []byte) (respBytes []byte, err error) {
|
||||
|
||||
// 发送 POST 请求
|
||||
httpReq, err := http.NewRequest("POST", url, bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create request failed: %w", err)
|
||||
}
|
||||
httpReq.Header.Set("Content-Type", "application/xml; charset=utf-8")
|
||||
|
||||
client := &http.Client{Transport: srv.transport, Timeout: 20 * time.Second}
|
||||
|
||||
// 执行请求
|
||||
httpResp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("send request failed: %w", err)
|
||||
}
|
||||
defer httpResp.Body.Close()
|
||||
|
||||
// 解析响应
|
||||
respBytes, err = io.ReadAll(httpResp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read response failed: %w", err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package srv
|
||||
|
||||
import (
|
||||
"plugins/wechat_redpack_v2/internal/wechat/utils"
|
||||
)
|
||||
|
||||
type Srv struct {
|
||||
*utils.MchConfig
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
package transfer
|
||||
|
||||
// 错误码常量定义
|
||||
const (
|
||||
ACCOUNT_FROZEN = "ACCOUNT_FROZEN"
|
||||
ACCOUNT_NOT_EXIST = "ACCOUNT_NOT_EXIST"
|
||||
BANK_CARD_ACCOUNT_ABNORMAL = "BANK_CARD_ACCOUNT_ABNORMAL"
|
||||
BANK_CARD_BANK_INFO_WRONG = "BANK_CARD_BANK_INFO_WRONG"
|
||||
BANK_CARD_CARD_INFO_WRONG = "BANK_CARD_CARD_INFO_WRONG"
|
||||
BANK_CARD_COLLECTIONS_ABOVE_QUOTA = "BANK_CARD_COLLECTIONS_ABOVE_QUOTA"
|
||||
BANK_CARD_PARAM_ERROR = "BANK_CARD_PARAM_ERROR"
|
||||
BANK_CARD_STATUS_ABNORMAL = "BANK_CARD_STATUS_ABNORMAL"
|
||||
BLOCK_B2C_USERLIMITAMOUNT_BSRULE_MONTH = "BLOCK_B2C_USERLIMITAMOUNT_BSRULE_MONTH"
|
||||
BLOCK_B2C_USERLIMITAMOUNT_MONTH = "BLOCK_B2C_USERLIMITAMOUNT_MONTH"
|
||||
DAY_RECEIVED_COUNT_EXCEED = "DAY_RECEIVED_COUNT_EXCEED"
|
||||
DAY_RECEIVED_QUOTA_EXCEED = "DAY_RECEIVED_QUOTA_EXCEED"
|
||||
EXCEEDED_ESTIMATED_AMOUNT = "EXCEEDED_ESTIMATED_AMOUNT"
|
||||
ID_CARD_NOT_CORRECT = "ID_CARD_NOT_CORRECT"
|
||||
MCH_CANCEL = "MCH_CANCEL"
|
||||
MERCHANT_REJECT = "MERCHANT_REJECT"
|
||||
MERCHANT_NOT_CONFIRM = "MERCHANT_NOT_CONFIRM"
|
||||
NAME_NOT_CORRECT = "NAME_NOT_CORRECT"
|
||||
OPENID_INVALID = "OPENID_INVALID"
|
||||
OTHER_FAIL_REASON_TYPE = "OTHER_FAIL_REASON_TYPE"
|
||||
OVERDUE_CLOSE = "OVERDUE_CLOSE"
|
||||
PAYEE_ACCOUNT_ABNORMAL = "PAYEE_ACCOUNT_ABNORMAL"
|
||||
PAYER_ACCOUNT_ABNORMAL = "PAYER_ACCOUNT_ABNORMAL"
|
||||
PRODUCT_AUTH_CHECK_FAIL = "PRODUCT_AUTH_CHECK_FAIL"
|
||||
REALNAME_ACCOUNT_RECEIVED_QUOTA_EXCEED = "REALNAME_ACCOUNT_RECEIVED_QUOTA_EXCEED"
|
||||
REAL_NAME_CHECK_FAIL = "REAL_NAME_CHECK_FAIL"
|
||||
RECEIVE_ACCOUNT_NOT_CONFIGURE = "RECEIVE_ACCOUNT_NOT_CONFIGURE"
|
||||
RESERVATION_INFO_NOT_MATCH = "RESERVATION_INFO_NOT_MATCH"
|
||||
RESERVATION_SCENE_NOT_MATCH = "RESERVATION_SCENE_NOT_MATCH"
|
||||
RESERVATION_STATE_INVALID = "RESERVATION_STATE_INVALID"
|
||||
TRANSFER_QUOTA_EXCEED = "TRANSFER_QUOTA_EXCEED"
|
||||
TRANSFER_REMARK_SET_FAIL = "TRANSFER_REMARK_SET_FAIL"
|
||||
TRANSFER_RISK = "TRANSFER_RISK"
|
||||
TRANSFER_SCENE_INVALID = "TRANSFER_SCENE_INVALID"
|
||||
TRANSFER_SCENE_UNAVAILABLE = "TRANSFER_SCENE_UNAVAILABLE"
|
||||
)
|
||||
|
||||
// 错误码到错误信息的映射
|
||||
var ErrorCodeMap = map[string]string{
|
||||
ACCOUNT_FROZEN: "该用户账户被冻结",
|
||||
ACCOUNT_NOT_EXIST: "该用户账户不存在",
|
||||
BANK_CARD_ACCOUNT_ABNORMAL: "银行卡已被销户、冻结、作废、挂失等致无法入账",
|
||||
BANK_CARD_BANK_INFO_WRONG: "登记的银行名称或分支行信息有误",
|
||||
BANK_CARD_CARD_INFO_WRONG: "银行卡户名或卡号有误",
|
||||
BANK_CARD_COLLECTIONS_ABOVE_QUOTA: "银行卡属二/三类卡,达到收款限额无法入账",
|
||||
BANK_CARD_PARAM_ERROR: "用户收款卡错误,请核实信息",
|
||||
BANK_CARD_STATUS_ABNORMAL: "银行卡状态异常,无法入账",
|
||||
BLOCK_B2C_USERLIMITAMOUNT_BSRULE_MONTH: "超出用户月转账收款限额,本月不支持继续向该用户付款",
|
||||
BLOCK_B2C_USERLIMITAMOUNT_MONTH: "用户账户存在风险收款受限,本月不支持继续向该用户付款",
|
||||
DAY_RECEIVED_COUNT_EXCEED: "超过用户日收款次数,核实产品设置是否准确",
|
||||
DAY_RECEIVED_QUOTA_EXCEED: "超过用户日收款额度,核实产品设置是否准确",
|
||||
EXCEEDED_ESTIMATED_AMOUNT: "转账金额超过预约金额范围,请检查",
|
||||
ID_CARD_NOT_CORRECT: "收款人身份证校验不通过,请核实信息",
|
||||
MCH_CANCEL: "商户撤销付款",
|
||||
MERCHANT_REJECT: "商户员工(转账验密人)已驳回转账",
|
||||
MERCHANT_NOT_CONFIRM: "商户员工(转账验密人)超时未验密",
|
||||
NAME_NOT_CORRECT: "收款人姓名校验不通过,请核实信息",
|
||||
OPENID_INVALID: "OpenID格式错误或者不属于商家公众账号",
|
||||
OTHER_FAIL_REASON_TYPE: "其它失败原因",
|
||||
OVERDUE_CLOSE: "超过系统重试期,系统自动关闭",
|
||||
PAYEE_ACCOUNT_ABNORMAL: "用户账户收款异常,请联系用户完善其在微信支付的身份信息以继续收款",
|
||||
PAYER_ACCOUNT_ABNORMAL: "商户账户付款受限,可前往商户平台获取解除功能限制指引",
|
||||
PRODUCT_AUTH_CHECK_FAIL: "未开通该权限或权限被冻结,请核实产品权限状态",
|
||||
REALNAME_ACCOUNT_RECEIVED_QUOTA_EXCEED: "用户账户收款受限,请引导用户在微信支付查看详情",
|
||||
REAL_NAME_CHECK_FAIL: "收款人未实名认证,需要用户完成微信实名认证",
|
||||
RECEIVE_ACCOUNT_NOT_CONFIGURE: "请前往商户平台-商家转账-前往功能-转账场景中添加",
|
||||
RESERVATION_INFO_NOT_MATCH: "转账信息,如用户OpenID等参数,与预约时传入的信息不一致,请检查",
|
||||
RESERVATION_SCENE_NOT_MATCH: "该预约单的转账场景与发起转账时传入的不同,请检查",
|
||||
RESERVATION_STATE_INVALID: "预约转账单状态异常,请检查",
|
||||
TRANSFER_QUOTA_EXCEED: "超过用户单笔收款额度,核实产品设置是否准确",
|
||||
TRANSFER_REMARK_SET_FAIL: "转账备注设置失败, 请调整后重新再试",
|
||||
TRANSFER_RISK: "该笔转账可能存在风险,已被微信拦截",
|
||||
TRANSFER_SCENE_INVALID: "你尚未获取该转账场景,请确认转账场景ID是否正确",
|
||||
TRANSFER_SCENE_UNAVAILABLE: "该转账场景暂不可用,请确认转账场景ID是否正确",
|
||||
}
|
||||
|
||||
func GetFailReasonMsg(failReason string) string {
|
||||
if msg, exists := ErrorCodeMap[failReason]; exists {
|
||||
return msg
|
||||
}
|
||||
return failReason
|
||||
}
|
||||
|
||||
// IsReset https://pay.weixin.qq.com/doc/v3/merchant/4013774966
|
||||
func IsReset(failReason string) bool {
|
||||
// 重置订单可再次申请转账, 超过系统重试期,系统自动关闭、商户员工(转账验密人)超时未验密
|
||||
return failReason == OVERDUE_CLOSE || failReason == MERCHANT_NOT_CONFIRM
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
package transfer
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type TransferBillStatus string
|
||||
|
||||
func (e TransferBillStatus) Ptr() *TransferBillStatus {
|
||||
return &e
|
||||
}
|
||||
|
||||
// ACCEPTED: 转账已受理
|
||||
// PROCESSING: 转账锁定资金中。如果一直停留在该状态,建议检查账户余额是否足够,如余额不足,可充值后再原单重试。
|
||||
// WAIT_USER_CONFIRM: 待收款用户确认,可拉起微信收款确认页面进行收款确认
|
||||
// TRANSFERING: 转账中,可拉起微信收款确认页面再次重试确认收款
|
||||
// SUCCESS: 转账成功
|
||||
// FAIL: 转账失败
|
||||
// CANCELING: 商户撤销请求受理成功,该笔转账正在撤销中
|
||||
// CANCELLED: 转账撤销完成
|
||||
const (
|
||||
TRANSFERBILLSTATUS_ACCEPTED TransferBillStatus = "ACCEPTED"
|
||||
TRANSFERBILLSTATUS_PROCESSING TransferBillStatus = "PROCESSING"
|
||||
TRANSFERBILLSTATUS_WAIT_USER_CONFIRM TransferBillStatus = "WAIT_USER_CONFIRM"
|
||||
TRANSFERBILLSTATUS_TRANSFERING TransferBillStatus = "TRANSFERING"
|
||||
TRANSFERBILLSTATUS_SUCCESS TransferBillStatus = "SUCCESS"
|
||||
TRANSFERBILLSTATUS_FAIL TransferBillStatus = "FAIL"
|
||||
TRANSFERBILLSTATUS_CANCELING TransferBillStatus = "CANCELING"
|
||||
TRANSFERBILLSTATUS_CANCELLED TransferBillStatus = "CANCELLED"
|
||||
)
|
||||
|
||||
var TransferBillStatusMap = map[TransferBillStatus]string{
|
||||
TRANSFERBILLSTATUS_ACCEPTED: "转账已受理",
|
||||
TRANSFERBILLSTATUS_PROCESSING: "转账锁定资金中,建议检查账户余额是否足够",
|
||||
TRANSFERBILLSTATUS_WAIT_USER_CONFIRM: "待收款用户确认",
|
||||
TRANSFERBILLSTATUS_TRANSFERING: "转账中",
|
||||
TRANSFERBILLSTATUS_SUCCESS: "转账成功",
|
||||
TRANSFERBILLSTATUS_FAIL: "转账失败",
|
||||
TRANSFERBILLSTATUS_CANCELING: "转账正在撤销中",
|
||||
TRANSFERBILLSTATUS_CANCELLED: "转账撤销完成",
|
||||
}
|
||||
|
||||
func (s TransferBillStatus) GetValue() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (s TransferBillStatus) GetText() string {
|
||||
if t, ok := TransferBillStatusMap[s]; ok {
|
||||
return t
|
||||
}
|
||||
return "未知状态"
|
||||
}
|
||||
|
||||
type TransferToUserRequest struct {
|
||||
Appid *string `json:"appid,omitempty"`
|
||||
OutBillNo *string `json:"out_bill_no,omitempty"`
|
||||
TransferSceneId *string `json:"transfer_scene_id,omitempty"`
|
||||
Openid *string `json:"openid,omitempty"`
|
||||
UserName *string `json:"user_name,omitempty"`
|
||||
TransferAmount *int64 `json:"transfer_amount,omitempty"`
|
||||
TransferRemark *string `json:"transfer_remark,omitempty"`
|
||||
NotifyUrl *string `json:"notify_url,omitempty"`
|
||||
UserRecvPerception *string `json:"user_recv_perception,omitempty"`
|
||||
TransferSceneReportInfos []TransferSceneReportInfo `json:"transfer_scene_report_infos,omitempty"`
|
||||
}
|
||||
|
||||
type TransferSceneReportInfo struct {
|
||||
InfoType *string `json:"info_type,omitempty"`
|
||||
InfoContent *string `json:"info_content,omitempty"`
|
||||
}
|
||||
|
||||
type TransferToUserResponse struct {
|
||||
OutBillNo *string `json:"out_bill_no,omitempty"` // 商户单号
|
||||
TransferBillNo *string `json:"transfer_bill_no,omitempty"` // 微信转账单号 string(64)
|
||||
CreateTime *string `json:"create_time,omitempty"` // 单据创建时间 单据受理成功时返回 格式为yyyy-MM-DDThh:mm:ss+TIMEZONE
|
||||
State *TransferBillStatus `json:"state,omitempty"` // 商家转账订单状态
|
||||
PackageInfo *string `json:"package_info,omitempty"` // 跳转领取页面的package信息
|
||||
}
|
||||
|
||||
type CancelTransferResponse struct {
|
||||
OutBillNo *string `json:"out_bill_no,omitempty"`
|
||||
TransferBillNo *string `json:"transfer_bill_no,omitempty"`
|
||||
State *string `json:"state,omitempty"` // 【单据状态】 CANCELING: 撤销中;CANCELLED:已撤销
|
||||
UpdateTime *string `json:"update_time,omitempty"`
|
||||
}
|
||||
|
||||
type CancelTransferRequest struct {
|
||||
OutBillNo *string `json:"out_bill_no,omitempty"`
|
||||
}
|
||||
|
||||
func (o *CancelTransferRequest) MarshalJSON() ([]byte, error) {
|
||||
type Alias CancelTransferRequest
|
||||
a := &struct {
|
||||
OutBillNo *string `json:"out_bill_no,omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
// 序列化时移除非 Body 字段
|
||||
OutBillNo: nil,
|
||||
Alias: (*Alias)(o),
|
||||
}
|
||||
return json.Marshal(a)
|
||||
}
|
||||
|
||||
type GetTransferBillByOutNoRequest struct {
|
||||
OutBillNo *string `json:"out_bill_no,omitempty"`
|
||||
}
|
||||
|
||||
func (o *GetTransferBillByOutNoRequest) MarshalJSON() ([]byte, error) {
|
||||
type Alias GetTransferBillByOutNoRequest
|
||||
a := &struct {
|
||||
OutBillNo *string `json:"out_bill_no,omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
// 序列化时移除非 Body 字段
|
||||
OutBillNo: nil,
|
||||
Alias: (*Alias)(o),
|
||||
}
|
||||
return json.Marshal(a)
|
||||
}
|
||||
|
||||
type TransferBillEntity struct {
|
||||
MchId *string `json:"mch_id,omitempty"` // 商户号
|
||||
OutBillNo *string `json:"out_bill_no,omitempty"` // 商户单号
|
||||
TransferBillNo *string `json:"transfer_bill_no,omitempty"` // 微信单号
|
||||
Appid *string `json:"appid,omitempty"` // 应用id
|
||||
State *TransferBillStatus `json:"state,omitempty"` // 商家转账订单状态
|
||||
TransferAmount *int64 `json:"transfer_amount,omitempty"` // 转账总金额,单位为“分”
|
||||
TransferRemark *string `json:"transfer_remark,omitempty"` // 转账备注
|
||||
FailReason *string `json:"fail_reason,omitempty"` // 单已失败或者已退资金时,会返回订单失败原因 https://pay.weixin.qq.com/doc/v3/merchant/4013774966
|
||||
Openid *string `json:"openid,omitempty"` // 用户在商户appid下的唯一标识
|
||||
UserName *string `json:"user_name,omitempty"` // 收款用户姓名
|
||||
CreateTime *string `json:"create_time,omitempty"` // 单据创建时间
|
||||
UpdateTime *string `json:"update_time,omitempty"` // 最后一次状态变更时间
|
||||
}
|
||||
|
||||
type GetTransferBillByNoRequest struct {
|
||||
TransferBillNo *string `json:"transfer_bill_no,omitempty"`
|
||||
}
|
||||
|
||||
func (o *GetTransferBillByNoRequest) MarshalJSON() ([]byte, error) {
|
||||
type Alias GetTransferBillByNoRequest
|
||||
a := &struct {
|
||||
TransferBillNo *string `json:"transfer_bill_no,omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
// 序列化时移除非 Body 字段
|
||||
TransferBillNo: nil,
|
||||
Alias: (*Alias)(o),
|
||||
}
|
||||
return json.Marshal(a)
|
||||
}
|
||||
|
||||
type TransferBillNotify struct {
|
||||
OutBillNo *string `json:"out_bill_no,omitempty"`
|
||||
TransferBillNo *string `json:"transfer_bill_no,omitempty"`
|
||||
State *TransferBillStatus `json:"state,omitempty"`
|
||||
MchId *string `json:"mch_id,omitempty"`
|
||||
TransferAmount *int64 `json:"transfer_amount,omitempty"`
|
||||
Openid *string `json:"openid,omitempty"`
|
||||
FailReason *string `json:"fail_reason,omitempty"`
|
||||
CreateTime *string `json:"create_time,omitempty"`
|
||||
UpdateTime *string `json:"update_time,omitempty"`
|
||||
}
|
||||
|
||||
// TransferErr {"code":"INVALID_REQUEST","message":"对应单号已超出重试期;请查单确认后决定是否换单请求"}
|
||||
type TransferErr struct {
|
||||
Code string `json:"code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func BuildTransferErr(body []byte) (*TransferErr, error) {
|
||||
ret := &TransferErr{}
|
||||
err := json.Unmarshal(body, &ret)
|
||||
return ret, err
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
package transfer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"plugins/wechat_redpack_v2/internal/wechat/srv"
|
||||
"plugins/wechat_redpack_v2/internal/wechat/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
transferToUserPath = "/v3/fund-app/mch-transfer/transfer-bills"
|
||||
transferCancelPath = "/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}/cancel"
|
||||
transferQueryByBillByOutNoPath = "/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}"
|
||||
transferQueryByTransferBillNoPath = "/v3/fund-app/mch-transfer/transfer-bills/transfer-bill-no/{transfer_bill_no}"
|
||||
)
|
||||
|
||||
type Transfer srv.Srv
|
||||
|
||||
// TransferToUser 转账 @link https://pay.weixin.qq.com/doc/v3/merchant/4012716434
|
||||
// 单个商户的接口频率限制为100次/s
|
||||
func (srv *Transfer) TransferToUser(_ context.Context, req *TransferToUserRequest) (response *TransferToUserResponse, err error) {
|
||||
|
||||
reqBody, err := json.Marshal(&req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
respBody, err := srv.Request(utils.Host, utils.MethodPOST, transferToUserPath, reqBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(respBody, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// TransferCancel 取消转账 @link https://pay.weixin.qq.com/doc/v3/merchant/4012716458
|
||||
func (srv *Transfer) TransferCancel(_ context.Context, req *CancelTransferRequest) (response *CancelTransferResponse, err error) {
|
||||
|
||||
reqBody, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path := strings.Replace(transferCancelPath, "{out_bill_no}", url.PathEscape(*req.OutBillNo), -1)
|
||||
|
||||
respBody, err := srv.Request(utils.Host, utils.MethodPOST, path, reqBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(respBody, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// TransferQueryByBillByOutNo 商户单号查询转账单 @link https://pay.weixin.qq.com/doc/v3/merchant/4012716437
|
||||
func (srv *Transfer) TransferQueryByBillByOutNo(_ context.Context, req *GetTransferBillByOutNoRequest) (response *TransferBillEntity, err error) {
|
||||
|
||||
path := strings.Replace(transferQueryByBillByOutNoPath, "{out_bill_no}", url.PathEscape(*req.OutBillNo), -1)
|
||||
|
||||
respBody, err := srv.Request(utils.Host, utils.MethodGET, path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(respBody, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// TransferQueryByTransferBillNo 微信单号查询转账单 @link https://pay.weixin.qq.com/doc/v3/merchant/4012716457
|
||||
func (srv *Transfer) TransferQueryByTransferBillNo(_ context.Context, req *GetTransferBillByNoRequest) (response *TransferBillEntity, err error) {
|
||||
|
||||
path := strings.Replace(transferQueryByTransferBillNoPath, "{transfer_bill_no}", url.PathEscape(*req.TransferBillNo), -1)
|
||||
|
||||
respBody, err := srv.Request(utils.Host, utils.MethodGET, path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(respBody, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// TransferNotify 转账通知 @link https://pay.weixin.qq.com/doc/v3/merchant/4012712115
|
||||
//
|
||||
// 转账完成或者转账失败,对应单据状态status的值为SUCCESS、CANCELLED和FAIL
|
||||
func (srv *Transfer) TransferNotify(_ context.Context, headers *http.Header, respBody []byte) (response *TransferBillEntity, err error) {
|
||||
|
||||
bizStr, err := srv.Verify(headers, respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = json.Unmarshal([]byte(bizStr), &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue