Compare commits

..

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

95 changed files with 586 additions and 3506 deletions

View File

@ -13,7 +13,7 @@ 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; \
@ -41,6 +41,18 @@ zltx_v2:
make build-linux name=zltx_v2 && \
make build-win name=zltx_v2
.PHONY: union_pay_cpn
union_pay_cpn:
make build-mac name=union_pay_cpn && \
make build-linux name=union_pay_cpn && \
make build-win name=union_pay_cpn
.PHONY: union_pay_redpack
union_pay_redpack:
make build-mac name=union_pay_redpack && \
make build-linux name=union_pay_redpack && \
make build-win name=union_pay_redpack
.PHONY: alipay_cpn
alipay_cpn:
make build-mac name=alipay_cpn && \
@ -65,23 +77,5 @@ wechat_redpack:
make build-linux name=wechat_redpack && \
make build-win name=wechat_redpack
.PHONY: wechat_redpack_v2
wechat_redpack_v2:
make build-mac name=wechat_redpack_v2 && \
make build-linux name=wechat_redpack_v2 && \
make build-win name=wechat_redpack_v2
.PHONY: union_pay_cpn
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-mac name=union_pay_redpack && \
make build-linux name=union_pay_redpack && \
make build-win name=union_pay_redpack
.PHONY: all
all: zltx_v1 zltx_card_v1 zltx_v2 alipay_cpn alipay_redpack wechat_cpn wechat_redpack wechat_redpack_v2
all: zltx_v1 zltx_card_v1 union_pay_cpn union_pay_redpack alipay_cpn alipay_redpack wechat_cpn wechat_redpack zltx_v2

View File

@ -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() {

View File

@ -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]

View File

@ -2,16 +2,5 @@ package main
// main 这只是一个演示
func main() {
//wechatOrderCpn()
//wechatQueryCpn()
//WechatRedPackV2Order()
//WechatRedPackV2Query()
WechatRedPackV2Notify()
//alipayOrderRedPack()
//alipayQueryRedPack()
//zltxQuery()
//zltxCardNotify()
alipayOrderRedPack()
}

View File

@ -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,7 +36,7 @@ func wechatOrderCpn() {
request := &proto.OrderRequest{
Config: getWechatCpnConf(),
Order: &proto.OrderRequest_Order{
OrderNo: "2024112215282589700100009",
OrderNo: "202411221528258970010009",
Account: "oknbq5tQyff_vCjjRjCJBElhF1og",
Extra: []byte(`{"app_id":"wx83fd6da8093f55b7","stock_creator_mchid":"1679625521"}`),
},

View File

@ -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: "19497351672832450564",
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)
}

View File

@ -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)
}

View File

@ -1,78 +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_1.so",
Tag: "zltx_card_v1_1",
Version: 1,
CookieKey: "zltx_card_v1_1",
CookieValue: "zltx_card_v1_1",
}
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
}
// 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")
}()
//req := &proto.NotifyRequest{
// Config: zlxtCardCf(),
// Queries: []byte(``),
// Headers: []byte(`{"Accept-Encoding":["gzip, deflate, br"],"Authorization":["MD5 appid=101,sign=1A821E1E6FA824C7099D7F17F58E1650"],"Connection":["close"],"Content-Length":["183"],"Content-Type":["application/json"],"Cookie":[""],"User-Agent":["GuzzleHttp/6.5.5 curl/7.69.1 PHP/7.2.34"],"X-Forwarded-For":["47.96.248.136"],"X-Real-Ip":["47.96.248.136"],"X-Remoteaddr":["172.18.0.1"]}`),
// Body: []byte(`{"merchantId":25943,"outTradeNo":"202506161700247580010056","tradeNo":"789175179564695553","status":"01","cardCode":"XPHrv0+uPVQOqfymz1jJAsLOOuGpfvgXi9RIF1m4tRCsDdvcZDDNY21M/22F56M1"`),
//}
// 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("Order err:%+v i:%d", err, i)
} else {
log.Printf("Order res:%+v i:%d", res, i)
}
}
}

3
go.mod
View File

@ -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
View File

@ -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=

View File

@ -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

View File

@ -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=

View File

@ -2,17 +2,13 @@ package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/carlmjohnson/requests"
"plugins/alipay_cpn/internal/po"
)
// 插件通信信息,若不对应则会报错panic
// https://opendocs.alipay.com/open/282/105997?pathHash=3f0dc947
// https://www.yuque.com/xv2p76/manual/wpg9cl
// 数字分行用户账号查询接口
// https://opendocs.alipay.com/pre-open/9128e8f4_alipay.user.dtbankcust.account.query?pathHash=1118bfe2
const (
Tag = "alipay_cpn"
Version = 1
@ -29,101 +25,63 @@ const (
type AlipayCpnService struct{}
func (s *AlipayCpnService) Order(ctx context.Context, request *proto.OrderRequest) (resp2 *proto.OrderResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = 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
}

View File

@ -2,7 +2,7 @@ package internal
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/stretchr/testify/assert"
@ -12,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())
}

View File

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

View File

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

View File

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

View File

@ -5,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

View File

@ -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=

View File

@ -2,9 +2,9 @@ package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/carlmjohnson/requests"
"plugins/alipay_redpack/internal/po"
"plugins/utils/alipay"
)
@ -27,111 +27,75 @@ const (
type AlipayRedPackService struct{}
func (s *AlipayRedPackService) Order(ctx context.Context, request *proto.OrderRequest) (resp2 *proto.OrderResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = 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
}

View File

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

View File

@ -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
}

View File

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

View File

@ -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

View File

@ -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=

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,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

View File

@ -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=

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,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

View File

@ -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=

View File

@ -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{

View File

@ -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])
}

View File

@ -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

View File

@ -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=

View File

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

View File

@ -19,68 +19,48 @@ const (
// WeChatRedPackService @link https://pay.weixin.qq.com/docs/merchant/apis/batch-transfer-to-balance/transfer-batch/initiate-batch-transfer.html
type WeChatRedPackService struct{}
func (p *WeChatRedPackService) Order(ctx context.Context, request *proto.OrderRequest) (resp2 *proto.OrderResponse, respErr error) {
defer func() {
if err := recover(); err != nil {
respErr = 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
}

View File

@ -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
)

View File

@ -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=

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +0,0 @@
package srv
import (
"plugins/wechat_redpack_v2/internal/wechat/utils"
)
type Srv struct {
*utils.MchConfig
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -1,60 +0,0 @@
package transfer
import (
"context"
"fmt"
"os"
"plugins/wechat_redpack_v2/internal/wechat/utils"
"testing"
)
func tr() *Transfer {
dir, err := os.Getwd()
if err != nil {
panic(err)
}
mchId := "1652322442"
certificateSerialNo := "7CCBCA9E6FA47845189FB8D212BA191EDE155613"
wechatPayPublicKeyId := "PUB_KEY_ID_0116523224422025061800192371001800"
filePath := fmt.Sprintf("%s/cert/cert/%s", dir, mchId)
c, err := utils.CreateMchConfig(
mchId, // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
certificateSerialNo, // 商户API证书序列号如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
fmt.Sprintf("%s/%s", filePath, "wechat_private_key.pem"), // 商户API证书私钥文件路径本地文件路径
wechatPayPublicKeyId, // 微信支付公钥ID如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
fmt.Sprintf("%s/%s", filePath, "pub_key.pem"), // 微信支付公钥文件路径,本地文件路径
"7e6eb4a5ebeed3cf61c693586b11d00b",
)
if err != nil {
panic(err)
}
return &Transfer{c}
}
func TestTransfer_Transfer(t *testing.T) {
req := TransferToUserRequest{
Appid: utils.String("wxe3bd59243545fa8a"),
OutBillNo: nil,
TransferSceneId: nil,
Openid: nil,
UserName: nil,
TransferAmount: nil,
TransferRemark: nil,
NotifyUrl: nil,
UserRecvPerception: nil,
TransferSceneReportInfos: nil,
}
gotResponse, err := tr().TransferToUser(context.Background(), &req)
if err != nil {
t.Errorf("Transfer.Transfer() error = %v", err)
return
}
t.Logf("Transfer.Transfer() = %v", gotResponse)
}

View File

@ -1,87 +0,0 @@
package utils
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"errors"
"fmt"
)
const (
keyLengthByte = 32 // AES-256 密钥长度(字节)
authTagLengthByte = 16 // GCM 认证标签长度(字节)
)
// AesUtil 用于微信支付 AES-256-GCM 解密的工具类
type AesUtil struct {
aesKey []byte // 32字节的AES密钥
}
// NewAesUtil 创建AesUtil实例验证密钥长度
func NewAesUtil(aesKey string) (*AesUtil, error) {
if len(aesKey) != keyLengthByte {
return nil, errors.New("无效的ApiV3Key长度应为32个字节")
}
return &AesUtil{aesKey: []byte(aesKey)}, nil
}
// DecryptToString 解密 AEAD_AES_256_GCM 加密的数据
// associatedData: 附加认证数据
// nonceStr: 随机数12字节
// ciphertext: 加密后的密文Base64编码
func (a *AesUtil) DecryptToString(associatedData, nonceStr, ciphertext string) (string, error) {
// 1. Base64解码密文
cipherBytes, err := base64.StdEncoding.DecodeString(ciphertext)
if err != nil {
return "", fmt.Errorf("密文Base64解码失败: %w", err)
}
// 2. 验证密文长度(需包含认证标签)
if len(cipherBytes) <= authTagLengthByte {
return "", errors.New("密文长度不足,无法解析认证标签")
}
// 3. 分离密文和认证标签GCM模式中认证标签通常附加在密文末尾
ctext := cipherBytes[:len(cipherBytes)-authTagLengthByte]
authTag := cipherBytes[len(cipherBytes)-authTagLengthByte:]
// 4. 初始化AES-GCM加密器
block, err := aes.NewCipher(a.aesKey)
if err != nil {
return "", fmt.Errorf("创建AES加密器失败: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf("创建GCM模式失败: %w", err)
}
// 5. 构建附加数据GCM的AAD
additionalData := []byte(associatedData)
// 6. 解密GCM模式会自动验证认证标签
plaintext, err := gcm.Open(nil, []byte(nonceStr), append(ctext, authTag...), additionalData)
if err != nil {
return "", fmt.Errorf("解密失败(可能是密钥错误或数据被篡改): %w", err)
}
return string(plaintext), nil
}
type Resource struct {
OriginalType string `json:"original_type"`
Algorithm string `json:"algorithm"`
Ciphertext string `json:"ciphertext"` // 如支付通知中的 encrypted_data
AssociatedData string `json:"associated_data"` // 微信支付回调的附加数据 通常为空字符串或回调相关信息
Nonce string `json:"nonce"` // 12字节字符串
}
type WxNotifyBody struct {
Id string `json:"id"`
CreateTime string `json:"create_time"`
ResourceType string `json:"resource_type"`
EventType string `json:"event_type"`
Summary string `json:"summary"`
Resource Resource `json:"resource"`
}

View File

@ -1,572 +0,0 @@
package utils
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"sort"
"strings"
"time"
)
const (
Host = "https://api.mch.weixin.qq.com"
MethodGET = "GET"
MethodPOST = "POST"
)
// MchConfig 商户信息配置用于调用商户API
// https://pay.weixin.qq.com/doc/v3/merchant/4012716434
//
// 引用微信支付工具库 参考:https://pay.weixin.qq.com/doc/v3/merchant/4015119334
type MchConfig struct {
mchId string // 商户号
certificateSerialNo string // 商户API证书序列号
privateKeyFilePath string // 商户API证书对应的私钥文件路径
wechatPayPublicKeyId string // 微信支付公钥ID
wechatPayPublicKeyFilePath string // 微信支付公钥文件路径
privateKey *rsa.PrivateKey // 商户API证书对应的私钥
wechatPayPublicKey *rsa.PublicKey // 微信支付公钥
aesKey string
}
// MchId 商户号
func (c *MchConfig) MchId() string {
return c.mchId
}
// CertificateSerialNo 商户API证书序列号
func (c *MchConfig) CertificateSerialNo() string {
return c.certificateSerialNo
}
// PrivateKey 商户API证书对应的私钥
func (c *MchConfig) PrivateKey() *rsa.PrivateKey {
return c.privateKey
}
// WechatPayPublicKeyId 微信支付公钥ID
func (c *MchConfig) WechatPayPublicKeyId() string {
return c.wechatPayPublicKeyId
}
// WechatPayPublicKey 微信支付公钥
func (c *MchConfig) WechatPayPublicKey() *rsa.PublicKey {
return c.wechatPayPublicKey
}
// CreateMchConfig MchConfig 构造函数
func CreateMchConfig(
mchId string,
certificateSerialNo string,
privateKeyFilePath string,
wechatPayPublicKeyId string,
wechatPayPublicKeyFilePath string,
aesKey string,
) (*MchConfig, error) {
mchConfig := &MchConfig{
mchId: mchId,
certificateSerialNo: certificateSerialNo,
privateKeyFilePath: privateKeyFilePath,
wechatPayPublicKeyId: wechatPayPublicKeyId,
wechatPayPublicKeyFilePath: wechatPayPublicKeyFilePath,
aesKey: aesKey,
}
privateKey, err := LoadPrivateKeyWithPath(mchConfig.privateKeyFilePath)
if err != nil {
return nil, err
}
mchConfig.privateKey = privateKey
wechatPayPublicKey, err := LoadPublicKeyWithPath(mchConfig.wechatPayPublicKeyFilePath)
if err != nil {
return nil, err
}
mchConfig.wechatPayPublicKey = wechatPayPublicKey
return mchConfig, nil
}
// LoadPrivateKey 通过私钥的文本内容加载私钥
func LoadPrivateKey(privateKeyStr string) (privateKey *rsa.PrivateKey, err error) {
block, _ := pem.Decode([]byte(privateKeyStr))
if block == nil {
return nil, fmt.Errorf("decode private key err")
}
if block.Type != "PRIVATE KEY" {
return nil, fmt.Errorf("the kind of PEM should be PRVATE KEY")
}
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("parse private key err:%s", err.Error())
}
privateKey, ok := key.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("not a RSA private key")
}
return privateKey, nil
}
// LoadPublicKey 通过公钥的文本内容加载公钥
func LoadPublicKey(publicKeyStr string) (publicKey *rsa.PublicKey, err error) {
block, _ := pem.Decode([]byte(publicKeyStr))
if block == nil {
return nil, errors.New("decode public key error")
}
if block.Type != "PUBLIC KEY" {
return nil, fmt.Errorf("the kind of PEM should be PUBLIC KEY")
}
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("parse public key err:%s", err.Error())
}
publicKey, ok := key.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("%s is not rsa public key", publicKeyStr)
}
return publicKey, nil
}
// LoadPrivateKeyWithPath 通过私钥的文件路径内容加载私钥
func LoadPrivateKeyWithPath(path string) (privateKey *rsa.PrivateKey, err error) {
privateKeyBytes, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read private pem file err:%s", err.Error())
}
return LoadPrivateKey(string(privateKeyBytes))
}
// LoadPublicKeyWithPath 通过公钥的文件路径加载公钥
func LoadPublicKeyWithPath(path string) (publicKey *rsa.PublicKey, err error) {
publicKeyBytes, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read certificate pem file err:%s", err.Error())
}
return LoadPublicKey(string(publicKeyBytes))
}
// EncryptOAEPWithPublicKey 使用 OAEP padding方式用公钥进行加密
func EncryptOAEPWithPublicKey(message string, publicKey *rsa.PublicKey) (ciphertext string, err error) {
if publicKey == nil {
return "", fmt.Errorf("you should input *rsa.PublicKey")
}
ciphertextByte, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, publicKey, []byte(message), nil)
if err != nil {
return "", fmt.Errorf("encrypt message with public key err:%s", err.Error())
}
ciphertext = base64.StdEncoding.EncodeToString(ciphertextByte)
return ciphertext, nil
}
// SignSHA256WithRSA 通过私钥对字符串以 SHA256WithRSA 算法生成签名信息
func SignSHA256WithRSA(source string, privateKey *rsa.PrivateKey) (signature string, err error) {
if privateKey == nil {
return "", fmt.Errorf("private key should not be nil")
}
h := crypto.Hash.New(crypto.SHA256)
_, err = h.Write([]byte(source))
if err != nil {
return "", nil
}
hashed := h.Sum(nil)
signatureByte, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(signatureByte), nil
}
// VerifySHA256WithRSA 通过公钥对字符串和签名结果以 SHA256WithRSA 验证签名有效性
func VerifySHA256WithRSA(source string, signature string, publicKey *rsa.PublicKey) error {
if publicKey == nil {
return fmt.Errorf("public key should not be nil")
}
sigBytes, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return fmt.Errorf("verify failed: signature is not base64 encoded")
}
hashed := sha256.Sum256([]byte(source))
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hashed[:], sigBytes)
if err != nil {
return fmt.Errorf("verify signature with public key error:%s", err.Error())
}
return nil
}
// GenerateNonce 生成一个长度为 NonceLength 的随机字符串(只包含大小写字母与数字)
func GenerateNonce() (string, error) {
const (
// NonceSymbols 随机字符串可用字符集
NonceSymbols = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
// NonceLength 随机字符串的长度
NonceLength = 32
)
bs := make([]byte, NonceLength)
_, err := rand.Read(bs)
if err != nil {
return "", err
}
symbolsByteLength := byte(len(NonceSymbols))
for i, b := range bs {
bs[i] = NonceSymbols[b%symbolsByteLength]
}
return string(bs), nil
}
// BuildAuthorization 构建请求头中的 Authorization 信息
func BuildAuthorization(
mchid string,
certificateSerialNo string,
privateKey *rsa.PrivateKey,
method string,
canonicalURL string,
body []byte,
) (string, error) {
const (
SignatureMessageFormat = "%s\n%s\n%d\n%s\n%s\n" // 数字签名原文格式
// HeaderAuthorizationFormat 请求头中的 Authorization 拼接格式
HeaderAuthorizationFormat = "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""
)
nonce, err := GenerateNonce()
if err != nil {
return "", err
}
timestamp := time.Now().Unix()
message := fmt.Sprintf(SignatureMessageFormat, method, canonicalURL, timestamp, nonce, body)
signature, err := SignSHA256WithRSA(message, privateKey)
if err != nil {
return "", err
}
authorization := fmt.Sprintf(
HeaderAuthorizationFormat,
mchid,
nonce,
timestamp,
certificateSerialNo,
signature,
)
return authorization, nil
}
// ExtractResponseBody 提取应答报文的 Body
func ExtractResponseBody(response *http.Response) ([]byte, error) {
if response == nil {
return nil, nil
}
if response.Body == nil {
return nil, nil
}
body, err := io.ReadAll(response.Body)
if err != nil {
return nil, fmt.Errorf("read response HttpBody err:[%s]", err.Error())
}
response.Body = io.NopCloser(bytes.NewBuffer(body))
return body, nil
}
const (
WechatPayTimestamp = "Wechatpay-Timestamp" // 微信支付回包时间戳
WechatPayNonce = "Wechatpay-Nonce" // 微信支付回包随机字符串
WechatPaySignature = "Wechatpay-Signature" // 微信支付回包签名信息
WechatPaySerial = "Wechatpay-Serial" // 微信支付回包平台序列号
RequestID = "Request-Id" // 微信支付回包请求ID
)
// ValidateResponse 验证微信支付回包的签名信息
func ValidateResponse(
wechatpayPublicKeyId string,
wechatpayPublicKey *rsa.PublicKey,
headers *http.Header,
body []byte,
) error {
requestID := headers.Get(RequestID)
timestampStr := headers.Get(WechatPayTimestamp)
serialNo := headers.Get(WechatPaySerial)
signature := headers.Get(WechatPaySignature)
nonce := headers.Get(WechatPayNonce)
if timestampStr == "" {
return fmt.Errorf("header头请求时间戳未传递数据")
}
if serialNo == "" {
return fmt.Errorf("header头请求平台序列号未传递数据")
}
if nonce == "" {
return fmt.Errorf("header头请求随机字符串未传递数据")
}
// 拒绝过期请求
//timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
//if err != nil {
// return fmt.Errorf("invalid timestamp: %v", err)
//}
//if time.Now().Sub(time.Unix(timestamp, 0)) > 5*time.Minute {
// return errors.New("invalid timestamp")
//}
if serialNo != wechatpayPublicKeyId {
return fmt.Errorf(
"serial-no mismatch: got %s, expected %s, request-id: %s",
serialNo,
wechatpayPublicKeyId,
requestID,
)
}
message := fmt.Sprintf("%s\n%s\n%s\n", timestampStr, nonce, body)
if err2 := VerifySHA256WithRSA(message, signature, wechatpayPublicKey); err2 != nil {
return fmt.Errorf("invalid signature: %v, request-id: %s", err2, requestID)
}
return nil
}
// ApiException 微信支付API错误异常发送HTTP请求成功但返回状态码不是 2XX 时抛出本异常
type ApiException struct {
HttpStatusCode int // 应答报文的 HTTP 状态码
HttpHeader http.Header // 应答报文的 Header 信息
HttpBody []byte // 应答报文的 Body 原文
ErrCode string // 微信支付回包的错误码
ErrMessage string // 微信支付回包的错误信息
}
func (c *ApiException) Error() string {
buf := bytes.NewBuffer(nil)
buf.WriteString(fmt.Sprintf("srv error:[StatusCode: %d, Body: %s", c.HttpStatusCode, string(c.HttpBody)))
if len(c.HttpHeader) > 0 {
buf.WriteString(" Header: ")
for key, value := range c.HttpHeader {
buf.WriteString(fmt.Sprintf("\n - %v=%v", key, value))
}
buf.WriteString("\n")
}
buf.WriteString("]")
return buf.String()
}
func (c *ApiException) StatusCode() int {
return c.HttpStatusCode
}
func (c *ApiException) Header() http.Header {
return c.HttpHeader
}
func (c *ApiException) Body() []byte {
return c.HttpBody
}
func (c *ApiException) ErrorCode() string {
return c.ErrCode
}
func (c *ApiException) ErrorMessage() string {
return c.ErrMessage
}
func NewApiException(statusCode int, header http.Header, body []byte) error {
ret := &ApiException{
HttpStatusCode: statusCode,
HttpHeader: header,
HttpBody: body,
}
bodyObject := map[string]interface{}{}
if err := json.Unmarshal(body, &bodyObject); err == nil {
if val, ok := bodyObject["code"]; ok {
ret.ErrCode = val.(string)
}
if val, ok := bodyObject["message"]; ok {
ret.ErrMessage = val.(string)
}
}
return ret
}
// Time 复制 time.Time 对象,并返回复制体的指针
func Time(t time.Time) *time.Time {
return &t
}
// String 复制 string 对象,并返回复制体的指针
func String(s string) *string {
return &s
}
// Bool 复制 bool 对象,并返回复制体的指针
func Bool(b bool) *bool {
return &b
}
// Float64 复制 float64 对象,并返回复制体的指针
func Float64(f float64) *float64 {
return &f
}
// Float32 复制 float32 对象,并返回复制体的指针
func Float32(f float32) *float32 {
return &f
}
// Int64 复制 int64 对象,并返回复制体的指针
func Int64(i int64) *int64 {
return &i
}
// Int32 复制 int64 对象,并返回复制体的指针
func Int32(i int32) *int32 {
return &i
}
func (srv *MchConfig) Request(host, method, path string, reqBody []byte) (response []byte, err error) {
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", srv.WechatPayPublicKeyId())
httpRequest.Header.Set("Content-Type", "application/json")
authorization, err := BuildAuthorization(
srv.MchId(),
srv.CertificateSerialNo(),
srv.PrivateKey(),
method,
reqUrl.Path,
reqBody,
)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
fmt.Printf("请求失败Error: %v", err)
return nil, err
}
respBody, err := ExtractResponseBody(httpResponse)
if err != nil {
fmt.Printf("获取应答失败Error: %v", err)
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = ValidateResponse(
srv.WechatPayPublicKeyId(),
srv.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
fmt.Printf("验证应答签名失败Error: %v", err)
return nil, err
}
return respBody, nil
}
return nil, &ApiException{
HttpStatusCode: httpResponse.StatusCode,
HttpHeader: httpResponse.Header,
HttpBody: respBody,
}
}
func (srv *MchConfig) Verify(headers *http.Header, respBody []byte) (string, error) {
if respBody == nil {
return "", fmt.Errorf("request HttpBody is nil")
}
err := ValidateResponse(
srv.WechatPayPublicKeyId(),
srv.WechatPayPublicKey(),
headers,
respBody,
)
if err != nil {
return "", err
}
var wxNotifyBody WxNotifyBody
if err = json.Unmarshal(respBody, &wxNotifyBody); err != nil {
return "", err
}
aesUtil, err := NewAesUtil(srv.aesKey)
if err != nil {
return "", err
}
return aesUtil.DecryptToString(wxNotifyBody.Resource.AssociatedData, wxNotifyBody.Resource.Nonce, wxNotifyBody.Resource.Ciphertext)
}
// BuildSortedQueryString 函数接受一个 map返回按照字段名排序后的 URL 键值对格式字符串
func BuildSortedQueryString(params map[string]any) string {
// 创建一个字符串切片,用于保存所有的键名
var keys []string
for key := range params {
keys = append(keys, key)
}
// 对键名进行 ASCII 字典顺序排序
sort.Strings(keys)
// 构建一个 URL 键值对字符串
var queryStrings []string
for _, key := range keys {
// 拼接 key=value
queryStrings = append(queryStrings, fmt.Sprintf("%s=%v", key, params[key]))
}
// 使用 & 连接所有的 key=value 对
return strings.Join(queryStrings, "&")
}
func Sha1(data string) string {
// 创建一个 SHA-1 哈希对象
hash := sha1.New()
// 写入数据
hash.Write([]byte(data))
// 计算并获取加密后的结果
hashBytes := hash.Sum(nil)
// 将结果转换为十六进制字符串
hashString := hex.EncodeToString(hashBytes)
// 打印加密后的 SHA-1 值
return hashString
}

View File

@ -1,111 +0,0 @@
package utils
import (
"crypto/rsa"
"encoding/json"
"fmt"
"reflect"
"testing"
)
func TestMchConfig_Request(t *testing.T) {
type fields struct {
mchId string
certificateSerialNo string
privateKeyFilePath string
wechatPayPublicKeyId string
wechatPayPublicKeyFilePath string
privateKey *rsa.PrivateKey
wechatPayPublicKey *rsa.PublicKey
}
type args struct {
host string
method string
path string
reqBody []byte
}
tests := []struct {
name string
fields fields
args args
wantResponse []byte
wantErr bool
}{
{
name: "test",
fields: fields{
mchId: "1652322442",
certificateSerialNo: "certificateSerialNo",
privateKeyFilePath: "privateKeyFilePath",
wechatPayPublicKeyId: "wechatPayPublicKeyId",
wechatPayPublicKeyFilePath: "wechatPayPublicKeyFilePath",
},
args: args{
host: "https://api.mch.weixin.qq.com",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
srv := &MchConfig{
mchId: tt.fields.mchId,
certificateSerialNo: tt.fields.certificateSerialNo,
privateKeyFilePath: tt.fields.privateKeyFilePath,
wechatPayPublicKeyId: tt.fields.wechatPayPublicKeyId,
wechatPayPublicKeyFilePath: tt.fields.wechatPayPublicKeyFilePath,
privateKey: tt.fields.privateKey,
wechatPayPublicKey: tt.fields.wechatPayPublicKey,
}
gotResponse, err := srv.Request(tt.args.host, tt.args.method, tt.args.path, tt.args.reqBody)
if (err != nil) != tt.wantErr {
t.Errorf("Request() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotResponse, tt.wantResponse) {
t.Errorf("Request() gotResponse = %v, want %v", gotResponse, tt.wantResponse)
}
})
}
}
func TestMDecryptToString(t *testing.T) {
jsonStr := ` {
"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"
}
}`
var wxNotifyBody WxNotifyBody
err := json.Unmarshal([]byte(jsonStr), &wxNotifyBody)
if err != nil {
fmt.Printf("JSON 解析失败: %v", err)
return
}
// 微信支付 ApiV3 密钥32字节
apiV3Key := "7e6eb4a5ebeed3cf61c693586b11d00b"
// 初始化解密工具
aesUtil, err := NewAesUtil(apiV3Key)
if err != nil {
fmt.Printf("NewAesUtil err: %v", err)
return
}
// 解密
plaintext, err := aesUtil.DecryptToString(wxNotifyBody.Resource.AssociatedData, wxNotifyBody.Resource.Nonce, wxNotifyBody.Resource.Ciphertext)
if err != nil {
panic(fmt.Sprintf("解密失败: %v", err))
}
fmt.Println("解密结果:", plaintext)
}

View File

@ -1,128 +0,0 @@
package internal
import (
"context"
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"net/http"
"plugins/wechat_redpack_v2/internal/wechat/srv/transfer"
"plugins/wechat_redpack_v2/internal/wechat/utils"
)
// 插件通信信息,若不对应则会报错panic
const (
Tag = "wechat_redpack_v2"
Version = 1
CookieKey = "wechat_redpack_v2"
CookieValue = "wechat_redpack_v2"
)
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)
}
}()
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())
}
c, err := Client(config)
if err != nil {
return nil, proto.ErrorConfigFail(err.Error())
}
resp, err := c.TransferToUser(ctx, req)
if err != nil {
return nil, p.err(err)
}
return orderResp(request.GetOrder(), resp)
}
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)
}
}()
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())
}
c, err := Client(config)
if err != nil {
return nil, proto.ErrorConfigFail(err.Error())
}
resp, err := c.TransferQueryByBillByOutNo(ctx, req)
if err != nil {
return nil, p.err(err)
}
return queryResp(request, resp)
}
func (p *WeChatRedPackService) 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)
}
}()
httpHeaders := make(http.Header)
if err := json.Unmarshal(request.Headers, &httpHeaders); err != nil {
return nil, proto.ErrorParamFail(fmt.Sprintf("headers Unmarshal err [%v]", err))
}
config, err := transConfig(request.Config)
if err != nil {
return nil, err
}
c, err := Client(config)
if err != nil {
return nil, proto.ErrorConfigFail(err.Error())
}
resp, err := c.TransferNotify(ctx, &httpHeaders, request.Body)
if err != nil {
return nil, proto.ErrorSignFail("err:%v", err)
}
return notifyResp(resp), nil
}
func (p *WeChatRedPackService) err(err error) error {
if e, ok := err.(*utils.ApiException); ok {
apiErr, err3 := transfer.BuildTransferErr(e.Body())
if err3 != nil {
return err3
}
return fmt.Errorf("请求微信API错误: %s", apiErr.Message)
}
return proto.ErrorRequestFail("请求微信返回错误:%v", err)
}

View File

@ -1,99 +0,0 @@
package internal
import (
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/go-playground/validator/v10"
"os"
"plugins/utils/helper"
"plugins/wechat_redpack_v2/internal/wechat/srv/transfer"
"plugins/wechat_redpack_v2/internal/wechat/utils"
"sync"
)
type manager struct {
once sync.Once
mutex sync.RWMutex
clients map[string]*transfer.Transfer
}
var instance manager
func init() {
instance = manager{
clients: make(map[string]*transfer.Transfer),
}
}
type Wechat struct {
MchID string `json:"mch_id" validate:"required"`
MchCertificateSerialNumber string `json:"mch_certificate_serial_number" validate:"required"`
WechatPayPublicKeyID string `json:"wechat_pay_public_key_id" validate:"required"`
MchApiV3Key string `json:"mch_api_v3_key" validate:"required"`
}
func (c *Wechat) Validate() error {
err := validator.New().Struct(c)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorConfigFail("配置有误:" + err.Error())
}
}
return nil
}
func Client(wx *Wechat) (*transfer.Transfer, error) {
if wx.MchID == "" {
return nil, fmt.Errorf("微信商户ID不能为空")
}
if wx.MchCertificateSerialNumber == "" {
return nil, fmt.Errorf("商户API证书序列号不能为空")
}
if wx.WechatPayPublicKeyID == "" {
return nil, fmt.Errorf("微信支付公钥ID不能为空")
}
if wx.MchApiV3Key == "" {
return nil, fmt.Errorf("微信apiv3key不能为空")
}
if client, ok := instance.clients[wx.MchID]; ok {
return client, nil
}
client, err := buildWx(wx)
if err != nil {
return nil, err
}
instance.clients[wx.MchID] = client
return client, nil
}
func buildWx(wx *Wechat) (*transfer.Transfer, error) {
dir, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("商户ID[%s]获取目的地址有误[%v]", wx.MchID, err)
}
filePath := fmt.Sprintf("%s/cert/wechat/%s", dir, wx.MchID)
if !helper.FileExists(filePath) {
return nil, fmt.Errorf("商户ID[%s]微信密钥证书信息不存在[%s],请联系技术人员处理", wx.MchID, filePath)
}
cc, err := utils.CreateMchConfig(
wx.MchID, // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
wx.MchCertificateSerialNumber, // 商户API证书序列号如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
fmt.Sprintf("%s/%s", filePath, "apiclient_key.pem"), // 商户API证书私钥文件路径本地文件路径
wx.WechatPayPublicKeyID, // 微信支付公钥ID如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
fmt.Sprintf("%s/%s", filePath, "pub_key.pem"), // 微信支付公钥文件路径,本地文件路径
wx.MchApiV3Key,
)
if err != nil {
return nil, fmt.Errorf("商户ID[%s]微信商户配置信息有误[%v]", wx.MchID, err)
}
return &transfer.Transfer{MchConfig: cc}, nil
}

View File

@ -1,29 +0,0 @@
package internal
import (
"testing"
)
func TestNewWechat(t *testing.T) {
//c := &conf.Bootstrap{
// Wechat: []*conf.Wechat{
// {
// MchID: "123456",
// MchCertificateSerialNumber: "123456",
// WechatPayPublicKeyID: "123456",
// Name: "test",
// },
// {
// MchID: "123456",
// MchCertificateSerialNumber: "123456",
// Name: "test",
// },
// },
//}
//got, err := NewWechat(c)
//if err != nil {
// t.Errorf("NewWechat() error = %v", err)
// return
//}
//t.Logf("NewWechat() = %v", got)
}

View File

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

View File

@ -3,7 +3,7 @@ module plugins/zltx
go 1.22.2
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.15
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

View File

@ -2,8 +2,6 @@ gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca h1:snL161P7OynMA8h
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=
gitea.cdlsxd.cn/sdk/plugin v1.0.15 h1:IcfTDd9K4QWX80gQuYurCavdsPyxi5tM5sW3MRGWdh8=
gitea.cdlsxd.cn/sdk/plugin v1.0.15/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/carlmjohnson/requests v0.24.2 h1:JDakhAmTIKL/qL/1P7Kkc2INGBJIkIFP6xUeUmPzLso=

View File

@ -3,7 +3,7 @@ module plugins/zltx_card
go 1.22.2
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.15
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

View File

@ -2,8 +2,6 @@ gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca h1:snL161P7OynMA8h
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=
gitea.cdlsxd.cn/sdk/plugin v1.0.15 h1:IcfTDd9K4QWX80gQuYurCavdsPyxi5tM5sW3MRGWdh8=
gitea.cdlsxd.cn/sdk/plugin v1.0.15/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/carlmjohnson/requests v0.24.2 h1:JDakhAmTIKL/qL/1P7Kkc2INGBJIkIFP6xUeUmPzLso=

View File

@ -3,17 +3,16 @@ module plugins/zltx_card_v1
go 1.22.2
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.24
gitea.cdlsxd.cn/sdk/plugin v1.0.14
github.com/go-playground/validator/v10 v10.22.0
github.com/hashicorp/go-plugin v1.6.1
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.9.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect

View File

@ -1,5 +1,9 @@
gitea.cdlsxd.cn/sdk/plugin v1.0.24 h1:B6sizQzFhb9Cin+iCYLKXiIBGyH/hCYQfl7CmkkF+ck=
gitea.cdlsxd.cn/sdk/plugin v1.0.24/go.mod h1:4fLMp/xB9GEBa3nJi62kXpHh7wnb9Lwjf0I8Vjaasx0=
gitea.cdlsxd.cn/sdk/plugin v1.0.12 h1:gA7zRagOro38rbRVIfFgGRuGOr+Wig8EjeqQkYedFXc=
gitea.cdlsxd.cn/sdk/plugin v1.0.12/go.mod h1:FLuWLP2QP2aBzI2HCdZ7tvl1ieMuMOpTWvItmNGCeGA=
gitea.cdlsxd.cn/sdk/plugin v1.0.13 h1:v9YUJci9Npb6svojARQkkbWpdsm74/X9g/uAECtrCJg=
gitea.cdlsxd.cn/sdk/plugin v1.0.13/go.mod h1:FLuWLP2QP2aBzI2HCdZ7tvl1ieMuMOpTWvItmNGCeGA=
gitea.cdlsxd.cn/sdk/plugin v1.0.14 h1:nBV4AJYdFTiwOdIwhfKuhHg60DCcHOZDFW5/W42bqGM=
gitea.cdlsxd.cn/sdk/plugin v1.0.14/go.mod h1:FLuWLP2QP2aBzI2HCdZ7tvl1ieMuMOpTWvItmNGCeGA=
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=
@ -8,8 +12,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=
@ -30,10 +32,6 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
@ -47,11 +45,9 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
@ -68,8 +64,7 @@ google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -7,7 +7,6 @@ import (
"gitea.cdlsxd.cn/sdk/plugin/dctw/v1/core"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/go-playground/validator/v10"
"net/http"
)
type Config struct {
@ -22,7 +21,7 @@ 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,7 +31,7 @@ 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 {
return nil, err
@ -55,13 +54,6 @@ func (c *Config) dctW() (*card.Card, error) {
return &card.Card{DctWServer: server}, nil
}
func Result(message string) *proto.Result {
return &proto.Result{
Status: proto.Status_ING,
Message: fmt.Sprintf("需人工排查处理:%s", message),
}
}
func (c *Config) orderReq(in *proto.OrderRequest) *card.Order {
return &card.Order{
Number: in.Order.Quantity,
@ -125,14 +117,5 @@ func notifyResp(resp *card.Notify, cardCode *card.CardCode) (*proto.NotifyRespon
},
Return: "success",
}
responseHeaders := make(http.Header)
responseHeaders.Set("Content-Type", "text/plain")
responseHeadersBytes, err := json.Marshal(responseHeaders)
if err != nil {
return nil, err
}
pb.Headers = string(responseHeadersBytes)
return pb, nil
}

View File

@ -12,29 +12,15 @@ import (
// 插件通信信息,若不对应则会报错panic
const (
Tag = "zltx_card_v1_1"
Tag = "zltx_card_v1"
Version = 1
CookieKey = "zltx_card_v1_1"
CookieValue = "zltx_card_v1_1"
CookieKey = "zltx_card_v1"
CookieValue = "zltx_card_v1"
)
type ZLTXCardV1Service struct{}
func (p *ZLTXCardV1Service) Order(ctx context.Context, request *proto.OrderRequest) (resp2 *proto.OrderResponse, respErr error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
// 继续执行
}
defer func() {
if err := recover(); err != nil {
respErr = fmt.Errorf("panic: %v", err)
}
}()
func (p *ZLTXCardV1Service) Order(ctx context.Context, request *proto.OrderRequest) (*proto.OrderResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
@ -47,26 +33,16 @@ func (p *ZLTXCardV1Service) Order(ctx context.Context, request *proto.OrderReque
resp, err := dctW.Order(ctx, c.orderReq(request))
if err != nil {
if proto.IsResponseFail(err) {
return &proto.OrderResponse{Result: Result(err.Error())}, nil
}
return nil, err
}
if !resp.GetCode().IsSuccess() {
return nil, proto.ErrorRequestFail(resp.Message)
return nil, fmt.Errorf(resp.Message)
}
return orderResp(request, resp), nil
}
func (p *ZLTXCardV1Service) 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 *ZLTXCardV1Service) Query(ctx context.Context, request *proto.QueryRequest) (*proto.QueryResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
@ -82,27 +58,13 @@ func (p *ZLTXCardV1Service) Query(ctx context.Context, request *proto.QueryReque
return nil, err
}
if !resp.GetCode().IsSuccess() {
return nil, proto.ErrorRequestFail(resp.Message)
return nil, fmt.Errorf(resp.Message)
}
return queryResp(request, resp, cardCode)
}
func (p *ZLTXCardV1Service) Notify(ctx context.Context, request *proto.NotifyRequest) (resp2 *proto.NotifyResponse, respErr error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
// 继续执行
}
defer func() {
if err := recover(); err != nil {
respErr = fmt.Errorf("panic: %v", err)
}
}()
func (p *ZLTXCardV1Service) Notify(ctx context.Context, request *proto.NotifyRequest) (*proto.NotifyResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
@ -110,7 +72,7 @@ func (p *ZLTXCardV1Service) Notify(ctx context.Context, request *proto.NotifyReq
httpHeaders := make(http.Header)
if err = json.Unmarshal(request.Headers, &httpHeaders); err != nil {
return nil, proto.ErrorParamFail(fmt.Sprintf("headers Unmarshal err [%v]", err))
return nil, fmt.Errorf("headers Unmarshal err [%v]", err)
}
newHeaders := make(http.Header)
for key, values := range httpHeaders {

View File

@ -9,25 +9,23 @@ import (
"testing"
)
var cardService = &ZLTXCardV1Service{}
var zltx = &ZLTXCardV1Service{}
func config() []byte {
//b := []byte(`{"app_id": "1", "app_key": "1e2bf7a04b8b1e6be5dc78d04e8639c9", "base_uri": "http://test.openapi.1688sup.cn", "notify_url": "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify"}`)
//return b
c := &Config{
AppId: "1",
AppKey: "1e2bf7a04b8b1e6be5dc78d04e8639c9",
BaseUri: "http://test.openapi.1688sup.cn",
NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify",
MerchantId: 23329,
}
//c := &Config{
// AppId: "101",
// AppKey: "95E7EC7D4A394FF8D11788E5E436DE99",
// BaseUri: "https://openapi.1688sup.com",
// AppId: "1",
// AppKey: "1e2bf7a04b8b1e6be5dc78d04e8639c9",
// BaseUri: "http://test.openapi.1688sup.cn",
// NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify",
// MerchantId: 25715,
// MerchantId: 23329,
//}
c := &Config{
AppId: "101",
AppKey: "95E7EC7D4A394FF8D11788E5E436DE99",
BaseUri: "https://openapi.1688sup.com",
NotifyUrl: "https://market.86698.cn/v1/order/direct/notify",
MerchantId: 25715,
}
marshal, _ := json.Marshal(c)
return marshal
}
@ -44,7 +42,7 @@ func TestOrder(t *testing.T) {
request := &proto.OrderRequest{
Config: config(),
Order: &proto.OrderRequest_Order{
OrderNo: "test_plugin_zltx_v1_card_4",
OrderNo: "test_plugin_zltx_v1_card_2",
Account: "",
Quantity: 1,
Extra: nil,
@ -56,7 +54,7 @@ func TestOrder(t *testing.T) {
}
t.Run("TestOrder", func(t *testing.T) {
got, err := cardService.Order(context.Background(), request)
got, err := zltx.Order(context.Background(), request)
if err != nil {
t.Errorf("Order() error = %v", err)
return
@ -70,14 +68,14 @@ func TestQuery(t *testing.T) {
request := &proto.QueryRequest{
Config: config(),
Order: &proto.QueryRequest_Order{
OrderNo: "test_plugin_zltx_v1_card_3",
OrderNo: "test_plugin_zltx_v1_card_2",
TradeNo: "",
Account: "",
Extra: nil,
},
}
t.Run("TestQuery", func(t *testing.T) {
got, err := cardService.Query(context.Background(), request)
got, err := zltx.Query(context.Background(), request)
if err != nil {
t.Errorf("Query() error = %v", err)
return
@ -91,12 +89,11 @@ func TestNotify(t *testing.T) {
in := &proto.NotifyRequest{
Config: config(),
Queries: nil,
Headers: []byte(`{"Authorization":["MD5 appid=1,sign=A419BC9E70FA82CC07030A041A4DEEC1"]}`),
Body: []byte(`{"merchantId":23369,"outTradeNo":"202501101143088400010016","tradeNo":"732200443106705409","status":"01","cardCode":"R7UVRxu5Zi8SockCksuZ3w=="}`),
Headers: []byte(`{"Accept-Encoding":["gzip, deflate, br"],"Authorization":["MD5 appid=101,sign=6ECA9F00E426FBE0137ACFA2920D058D"],"Connection":["close"],"Content-Length":["140"],"Content-Type":["application/json"],"Cookie":[""],"User-Agent":["GuzzleHttp/6.5.5 curl/7.69.1 PHP/7.2.34"],"X-Remoteaddr":["172.21.0.1"]}`),
Body: []byte(`{"merchantId":25715,"outTradeNo":"202411261524470700010001","tradeNo":"715948765692968961","status":"01","cardCode":"AlT5ZLb4VwYbKqTudDcyR4WZzrOLFtMUD00HNNvBFs0="}`),
}
t.Run("TestNotify", func(t *testing.T) {
got, err := cardService.Notify(context.Background(), in)
got, err := zltx.Notify(context.Background(), in)
if !assert.Nil(t, err) {
t.Errorf("Notify() error = %v", err)
return

View File

@ -3,17 +3,16 @@ module plugins/zltx_v1
go 1.22.2
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.20
gitea.cdlsxd.cn/sdk/plugin v1.0.14
github.com/go-playground/validator/v10 v10.22.0
github.com/hashicorp/go-plugin v1.6.1
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.9.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect

View File

@ -1,5 +1,11 @@
gitea.cdlsxd.cn/sdk/plugin v1.0.20 h1:oAcqfHiOHoa+f9w6BPbvY9EZ0pwQ1mo3dD+YJD8OpYo=
gitea.cdlsxd.cn/sdk/plugin v1.0.20/go.mod h1:W2zpoqi70a9X7n3WCLcZ0sjxXTPgYhiJW36FO+qKlb0=
gitea.cdlsxd.cn/sdk/plugin v1.0.11 h1:NMMgw2p7ZF7EbWjkSO7ttM62BLhsG0bGmPdGGS1gpJc=
gitea.cdlsxd.cn/sdk/plugin v1.0.11/go.mod h1:FLuWLP2QP2aBzI2HCdZ7tvl1ieMuMOpTWvItmNGCeGA=
gitea.cdlsxd.cn/sdk/plugin v1.0.12 h1:gA7zRagOro38rbRVIfFgGRuGOr+Wig8EjeqQkYedFXc=
gitea.cdlsxd.cn/sdk/plugin v1.0.12/go.mod h1:FLuWLP2QP2aBzI2HCdZ7tvl1ieMuMOpTWvItmNGCeGA=
gitea.cdlsxd.cn/sdk/plugin v1.0.13 h1:v9YUJci9Npb6svojARQkkbWpdsm74/X9g/uAECtrCJg=
gitea.cdlsxd.cn/sdk/plugin v1.0.13/go.mod h1:FLuWLP2QP2aBzI2HCdZ7tvl1ieMuMOpTWvItmNGCeGA=
gitea.cdlsxd.cn/sdk/plugin v1.0.14 h1:nBV4AJYdFTiwOdIwhfKuhHg60DCcHOZDFW5/W42bqGM=
gitea.cdlsxd.cn/sdk/plugin v1.0.14/go.mod h1:FLuWLP2QP2aBzI2HCdZ7tvl1ieMuMOpTWvItmNGCeGA=
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=
@ -8,8 +14,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=
@ -30,10 +34,6 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
@ -47,11 +47,9 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
@ -68,8 +66,7 @@ google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -8,7 +8,6 @@ import (
"gitea.cdlsxd.cn/sdk/plugin/proto"
"gitea.cdlsxd.cn/sdk/plugin/utils"
"github.com/go-playground/validator/v10"
"net/http"
"regexp"
)
@ -21,9 +20,10 @@ type Config struct {
}
func (c *Config) validate() error {
if err := validator.New().Struct(c); err != nil {
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
@ -31,15 +31,13 @@ func (c *Config) validate() error {
func transConfig(config []byte) (*Config, error) {
var c Config
if err := json.Unmarshal(config, &c); err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("配置参数解析失败: %v,参数:%s", err, string(config)))
}
if err := c.validate(); err != nil {
err := json.Unmarshal(config, &c)
if err != nil {
return nil, err
}
if err = c.validate(); err != nil {
return nil, err
}
return &c, nil
}
@ -54,13 +52,6 @@ func (c *Config) server() (*core.DctWServer, error) {
)
}
func Result(message string) *proto.Result {
return &proto.Result{
Status: proto.Status_ING,
Message: fmt.Sprintf("需人工排查处理:%s", message),
}
}
func (c *Config) orderReq(in *proto.OrderRequest) (*direct.Order, error) {
// 账号类型(1:手机号 2:QQ号 其他0)
accountType := int8(0)
@ -70,14 +61,13 @@ func (c *Config) orderReq(in *proto.OrderRequest) (*direct.Order, error) {
qqPattern := `^[1-9][0-9]{4,11}$`
qqRegex, err := regexp.Compile(qqPattern)
if err != nil {
return nil, proto.ErrorParamFail(fmt.Sprintf("正则表达式编译失败: %v", err))
return nil, fmt.Errorf("正则表达式编译失败: %v", err)
}
if qqRegex.MatchString(in.Order.Account) {
accountType = 2
}
}
d := &direct.Order{
return &direct.Order{
Number: in.Order.Quantity,
MerchantId: c.MerchantId,
OutTradeNo: in.Order.OrderNo,
@ -86,12 +76,7 @@ func (c *Config) orderReq(in *proto.OrderRequest) (*direct.Order, error) {
RechargeAccount: in.Order.Account,
NotifyUrl: c.NotifyUrl,
Version: "1.0",
}
if in.Order.Extra != nil {
d.ExtendParameter = string(in.Order.Extra)
}
return d, nil
}, nil
}
func orderResp(request *proto.OrderRequest, resp *direct.OrderResp) *proto.OrderResponse {
@ -116,12 +101,8 @@ func (c *Config) queryReq(in *proto.QueryRequest) *direct.Query {
}
func queryResp(request *proto.QueryRequest, resp *direct.QueryResp) (*proto.QueryResponse, error) {
data, err := json.Marshal(resp)
if err != nil {
return nil, err
}
return &proto.QueryResponse{
data, _ := json.Marshal(resp)
pb := &proto.QueryResponse{
Result: &proto.Result{
Status: resp.Status.GetOrderStatus(),
OrderNo: request.Order.OrderNo,
@ -130,15 +111,12 @@ func queryResp(request *proto.QueryRequest, resp *direct.QueryResp) (*proto.Quer
Data: data,
Extra: nil,
},
}, nil
}
return pb, nil
}
func notifyResp(resp *direct.Notify) (*proto.NotifyResponse, error) {
data, err := json.Marshal(resp)
if err != nil {
return nil, err
}
data, _ := json.Marshal(resp)
pb := &proto.NotifyResponse{
Result: &proto.Result{
Status: resp.Status.GetOrderStatus(),
@ -150,16 +128,5 @@ func notifyResp(resp *direct.Notify) (*proto.NotifyResponse, error) {
},
Return: "success",
}
responseHeaders := make(http.Header)
responseHeaders.Set("Content-Type", "text/plain")
responseHeadersBytes, err := json.Marshal(responseHeaders)
if err != nil {
return nil, err
}
pb.Headers = string(responseHeadersBytes)
return pb, nil
}

View File

@ -21,14 +21,7 @@ const (
type ZLTXV1Service struct{}
func (p *ZLTXV1Service) 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 *ZLTXV1Service) Order(ctx context.Context, request *proto.OrderRequest) (*proto.OrderResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
@ -43,30 +36,19 @@ func (p *ZLTXV1Service) Order(ctx context.Context, request *proto.OrderRequest)
if err != nil {
return nil, err
}
a := &direct.Direct{DctWServer: server}
resp, err := a.Order(ctx, req)
if err != nil {
if proto.IsResponseFail(err) {
return &proto.OrderResponse{Result: Result(err.Error())}, nil
}
return nil, err
}
if !resp.GetCode().IsSuccess() {
return nil, proto.ErrorRequestFail(resp.Message)
return nil, fmt.Errorf(resp.Message)
}
return orderResp(request, resp), nil
}
func (p *ZLTXV1Service) 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 *ZLTXV1Service) Query(ctx context.Context, request *proto.QueryRequest) (*proto.QueryResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
@ -83,20 +65,13 @@ func (p *ZLTXV1Service) Query(ctx context.Context, request *proto.QueryRequest)
return nil, err
}
if !resp.GetCode().IsSuccess() {
return nil, proto.ErrorRequestFail(resp.Message)
return nil, fmt.Errorf(resp.Message)
}
return queryResp(request, resp)
}
func (p *ZLTXV1Service) 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 *ZLTXV1Service) Notify(ctx context.Context, request *proto.NotifyRequest) (*proto.NotifyResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
@ -109,7 +84,7 @@ func (p *ZLTXV1Service) Notify(ctx context.Context, request *proto.NotifyRequest
httpHeaders := make(http.Header)
if err = json.Unmarshal(request.Headers, &httpHeaders); err != nil {
return nil, proto.ErrorParamFail(fmt.Sprintf("headers Unmarshal err [%v]", err))
return nil, fmt.Errorf("headers Unmarshal err [%v]", err)
}
newHeaders := make(http.Header)
for key, values := range httpHeaders {

View File

@ -6,27 +6,26 @@ import (
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/stretchr/testify/assert"
"net/http"
"testing"
)
var srv = &ZLTXV1Service{}
var zltx = &ZLTXV1Service{}
func config() []byte {
c := &Config{
AppId: "1",
AppKey: "1e2bf7a04b8b1e6be5dc78d04e8639c9",
BaseUri: "http://test.openapi.1688sup.cn",
NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify",
MerchantId: 23369, // 23329
}
//c := &Config{
// AppId: "101",
// AppKey: "95E7EC7D4A394FF8D11788E5E436DE99",
// BaseUri: "https://openapi.1688sup.com",
// AppId: "1",
// AppKey: "1e2bf7a04b8b1e6be5dc78d04e8639c9",
// BaseUri: "http://test.openapi.1688sup.cn",
// NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify",
// MerchantId: 25715,
// MerchantId: 25537,
//}
c := &Config{
AppId: "101",
AppKey: "5k8bJV6axIukpkwz5vdte8QCw5etlC+txi+Nu69nIhOMN4VhmcNgf/THdllpt0jO",
BaseUri: "https://openapi.1688sup.com",
NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify",
MerchantId: 25537,
}
marshal, _ := json.Marshal(c)
return marshal
}
@ -40,28 +39,22 @@ func TestConfig(t *testing.T) {
}
func TestOrder(t *testing.T) {
//d := []byte(`{"jdSmsCode":"123456"}`)
//s := base64.StdEncoding.EncodeToString(d)
//t.Logf("%s\n", s)
request := &proto.OrderRequest{
Config: config(),
Order: &proto.OrderRequest_Order{
OrderNo: "202501131732090710010002",
Account: "18666173766",
OrderNo: "test_plugin_zltx_v1_direct_1",
Account: "583989020@qq.com",
Quantity: 1,
//Extra: []byte(`{"jdCode":"123456"}`), // 京东e卡官方拓展参数
Extra: []byte(`{"jdSmsCode":"123456"}`), // 京东e卡专票拓展参数
Extra: nil,
},
Product: &proto.OrderRequest_Product{
ProductNo: "360", // 101 2255 360
ProductNo: "101",
Extra: []byte(`{}`),
},
}
//t.Logf("%s\n", request.String())
//return
t.Run("TestOrder", func(t *testing.T) {
got, err := srv.Order(context.Background(), request)
got, err := zltx.Order(context.Background(), request)
if err != nil {
t.Errorf("Order() error = %v", err)
return
@ -72,9 +65,8 @@ func TestOrder(t *testing.T) {
}
func TestQuery(t *testing.T) {
c := config()
request := &proto.QueryRequest{
Config: c,
Config: config(),
Order: &proto.QueryRequest_Order{
OrderNo: "test_plugin_zltx_v1_direct_1",
TradeNo: "",
@ -83,7 +75,7 @@ func TestQuery(t *testing.T) {
},
}
t.Run("TestQuery", func(t *testing.T) {
got, err := srv.Query(context.Background(), request)
got, err := zltx.Query(context.Background(), request)
if err != nil {
t.Errorf("Query() error = %v", err)
return
@ -97,26 +89,15 @@ func TestNotify(t *testing.T) {
in := &proto.NotifyRequest{
Config: config(),
Queries: nil,
Headers: []byte(`{"Authorization":["MD5 appid=1,sign=A419BC9E70FA82CC07030A041A4DEEC1"]}`),
Body: []byte(`{"merchantId":23369,"outTradeNo":"202501101143088400010016","tradeNo":"732200443106705409","status":"01","cardCode":"R7UVRxu5Zi8SockCksuZ3w=="}`),
Headers: []byte(`{"Accept-Encoding":["gzip, deflate, br"],"Authorization":["MD5 appid=1,sign=642F82644ABB88E73C04F7881B93E6FA"],"Connection":["close"],"Content-Length":["102"],"Content-Type":["application/json"],"Cookie":[""],"User-Agent":["GuzzleHttp/6.5.5 curl/7.69.1 PHP/7.2.34"],"X-Remoteaddr":["172.21.0.1"]}`),
Body: []byte(`{"merchantId":"25537","outTradeNo":"test_zltx_direct_2","status":"03","rechargeAccount":"18666666666"}`),
}
t.Run("TestNotify", func(t *testing.T) {
got, err := srv.Notify(context.Background(), in)
got, err := zltx.Notify(context.Background(), in)
if !assert.Nil(t, err) {
t.Errorf("Notify() error = %v", err)
return
}
responseHeaders := make(http.Header)
err = json.Unmarshal([]byte(got.Headers), &responseHeaders)
if err != nil {
t.Error(err)
return
}
for s, strings := range responseHeaders {
t.Logf("%s: %+v", s, strings)
}
fmt.Printf("%+v\n", got)
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})

View File

@ -5,8 +5,9 @@ go 1.22.2
replace plugins/utils => ../../utils
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.18
gitee.com/chengdu_blue_brothers/openapi-go-sdk-v2 v1.0.3
gitea.cdlsxd.cn/sdk/plugin v1.0.6
gitee.com/chengdu_blue_brothers/openapi-go-sdk-v2 v0.0.0-20240919023950-493c464e0ed7
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
@ -17,7 +18,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-kratos/kratos/v2 v2.8.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect

View File

@ -1,17 +1,19 @@
gitea.cdlsxd.cn/sdk/plugin v1.0.18 h1:YgVJmCSSEu8JAniXlT1rI+h0w3EEGDRWLFqjk/5xBQY=
gitea.cdlsxd.cn/sdk/plugin v1.0.18/go.mod h1:O/bYQWg1o9g/cBq9qNA3kLIpuPt7VDZqj1bPE6s04NM=
gitee.com/chengdu_blue_brothers/openapi-go-sdk-v2 v1.0.3 h1:vzHsHkJwFvobFxVlHQM8lEZY4h07TqR6cakjTqDHCQQ=
gitee.com/chengdu_blue_brothers/openapi-go-sdk-v2 v1.0.3/go.mod h1:aS6ecVHvGLGzYYFIhxBrRWmq69ifo6pt1G92QblSdQQ=
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=
gitee.com/chengdu_blue_brothers/openapi-go-sdk-v2 v0.0.0-20240919023950-493c464e0ed7 h1:2wOzkUfS17P6U/i6CWFjLyXrdLMnW3osk897ZfOUCxY=
gitee.com/chengdu_blue_brothers/openapi-go-sdk-v2 v0.0.0-20240919023950-493c464e0ed7/go.mod h1:aS6ecVHvGLGzYYFIhxBrRWmq69ifo6pt1G92QblSdQQ=
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=
@ -36,10 +38,6 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5i
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
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=
@ -57,8 +55,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=
@ -78,8 +74,7 @@ google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -2,27 +2,23 @@ package internal
import (
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"gitee.com/chengdu_blue_brothers/openapi-go-sdk-v2/api"
"gitee.com/chengdu_blue_brothers/openapi-go-sdk-v2/notify"
"gitee.com/chengdu_blue_brothers/openapi-go-sdk-v2/sdk"
"github.com/go-playground/validator/v10"
"net/http"
"plugins/zltxv2/internal/vo"
)
type Config struct {
AppId string `json:"app_id" validate:"required"`
AppKey string `json:"app_key" validate:"required"`
BaseUri string `json:"base_uri" validate:"required"`
AppId string `json:"app_id"`
AppKey string `json:"app_key"`
BaseUri string `json:"base_uri"`
NotifyUrl string `json:"notify_url"`
}
type Card struct {
Number string `json:"number"`
Password string `json:"password"`
Deadline string `json:"deadline"`
}
func (c *Card) ToJson() []byte {
@ -30,49 +26,27 @@ func (c *Card) ToJson() []byte {
return bytes
}
func (c *Config) validate() error {
if err := validator.New().Struct(c); err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
}
}
return nil
}
func transConfig(config []byte) (*Config, error) {
var c Config
if err := json.Unmarshal(config, &c); err != nil {
return nil, proto.ErrorConfigFail(err.Error())
}
if err := c.validate(); err != nil {
err := json.Unmarshal(config, &c)
if err != nil {
return nil, err
}
return &c, nil
}
func (c *Config) client() (*sdk.Client, error) {
scheme := getScheme(c.BaseUri)
domain, err := getDomain(c.BaseUri, scheme)
if err != nil {
return nil, proto.ErrorConfigFail(err.Error())
return nil, err
}
cl, err := sdk.NewClientWithAccessKey(c.AppKey, scheme, domain)
if err != nil {
return nil, proto.ErrorConfigFail(err.Error())
}
return cl, nil
return sdk.NewClientWithAccessKey(c.AppKey, scheme, domain)
}
func (c *Config) orderReq(in *proto.OrderRequest) *api.OrderCreateReq {
var accountType vo.AccountType
req := &api.OrderCreateReq{
return &api.OrderCreateReq{
OutTradeNo: in.Order.OrderNo, // 商户侧订单号长度只能是1-64位
ProductId: in.Product.ProductNo, // 产品编码不能为空
Number: int(in.Order.Quantity), // 部分商品存在限购数量,具体可联系商务
@ -83,8 +57,6 @@ func (c *Config) orderReq(in *proto.OrderRequest) *api.OrderCreateReq {
Attach: "", // 透传参数,当传了此参数时,查询和回调接口中将原样携带此参数
UnitPrice: 0, // 交易商品单价单位最多保留4位小数 - 0或不传表示不限制 - 当此价格小于采购价时,下单失败
}
return req
}
func orderResp(request *proto.OrderRequest, resp *api.OrderCreateResp) *proto.OrderResponse {
@ -109,9 +81,8 @@ func (c *Config) queryReq(in *proto.QueryRequest) *api.OrderQueryReq {
func queryResp(appKey string, request *proto.QueryRequest, resp *api.OrderQueryResp) (*proto.QueryResponse, error) {
data, err := json.Marshal(resp)
if err != nil {
return nil, proto.ErrorResponseFail(err.Error())
return nil, err
}
status := vo.OrderStatus(resp.TradeStatus)
pb := &proto.QueryResponse{
Result: &proto.Result{
@ -123,7 +94,6 @@ func queryResp(appKey string, request *proto.QueryRequest, resp *api.OrderQueryR
Extra: nil,
},
}
if len(resp.Cards) > 0 {
c, err := getQueryCard(appKey, &resp.Cards[0])
if err != nil {
@ -131,34 +101,28 @@ func queryResp(appKey string, request *proto.QueryRequest, resp *api.OrderQueryR
}
pb.Result.Extra = c.ToJson()
}
return pb, nil
}
func getQueryCard(appKey string, card *api.Cards) (*Card, error) {
car := &Card{
Deadline: card.Deadline,
}
car := &Card{}
t := vo.CardType(card.CardType)
if t.IsPwdNo() {
pwd, err := decryptAES(card.Pwd, appKey)
if err != nil {
return nil, proto.ErrorResponseFail(err.Error())
return nil, err
}
no, err := decryptAES(card.No, appKey)
if err != nil {
return nil, proto.ErrorResponseFail(err.Error())
return nil, err
}
car.Password = pwd
car.Number = no
} else if t.IsPwd() {
pwd, err := decryptAES(card.Pwd, appKey)
if err != nil {
return nil, proto.ErrorResponseFail(err.Error())
return nil, err
}
car.Password = pwd
} else if t.IsUrl() {
// 无
@ -169,9 +133,8 @@ func getQueryCard(appKey string, card *api.Cards) (*Card, error) {
func notifyResp(appKey string, resp *notify.OrderReq) (*proto.NotifyResponse, error) {
data, err := json.Marshal(resp)
if err != nil {
return nil, proto.ErrorResponseFail(err.Error())
return nil, err
}
status := vo.OrderStatus(resp.TradeStatus)
pb := &proto.NotifyResponse{
Result: &proto.Result{
@ -182,26 +145,13 @@ func notifyResp(appKey string, resp *notify.OrderReq) (*proto.NotifyResponse, er
Data: data,
Extra: nil,
},
Return: `{"code": "SUCCESS"}`,
Return: "SUCCESS",
}
responseHeaders := make(http.Header)
responseHeaders.Set("Content-Type", "application/json")
responseHeadersBytes, err := json.Marshal(responseHeaders)
if err != nil {
return nil, err
}
pb.Headers = string(responseHeadersBytes)
b, err := getNotifyCard(appKey, resp.Cards)
if err != nil {
return nil, err
}
pb.Result.Extra = b
return pb, nil
}
@ -209,45 +159,28 @@ func getNotifyCard(appKey string, cards []notify.Cards) ([]byte, error) {
if len(cards) == 0 {
return nil, nil
}
card := cards[0]
car := &Card{
Deadline: card.Deadline,
}
car := &Card{}
t := vo.CardType(card.CardType)
if t.IsPwdNo() {
pwd, err := decryptAES(card.Pwd, appKey)
if err != nil {
return nil, proto.ErrorResponseFail(err.Error())
return nil, err
}
no, err := decryptAES(card.No, appKey)
if err != nil {
return nil, proto.ErrorResponseFail(err.Error())
return nil, err
}
car.Password = pwd
car.Number = no
} else if t.IsPwd() {
pwd, err := decryptAES(card.Pwd, appKey)
if err != nil {
return nil, proto.ErrorResponseFail(err.Error())
return nil, err
}
car.Password = pwd
} else if t.IsUrl() {
if len(card.Url) == 0 {
return nil, proto.ErrorResponseFail("url is empty")
}
pwd, err := decryptAES(card.Url, appKey)
if err != nil {
return nil, proto.ErrorResponseFail(err.Error())
}
car.Password = pwd
// 无
}
return car.ToJson(), nil
}

View File

@ -15,8 +15,6 @@ func (o AccountType) AccountType(account string) AccountType {
return AccountTypeDefault
} else if helper.IsPhoneNumber(account) {
return AccountTypePhone
} else if helper.IsValidQQ(account) {
return AccountTypeQQ
}
return AccountTypeDefault
}

View File

@ -10,6 +10,7 @@ import (
"gitee.com/chengdu_blue_brothers/openapi-go-sdk-v2/notify"
"io"
"net/http"
"strings"
)
// 插件通信信息,若不对应则会报错panic
@ -22,49 +23,31 @@ const (
type ZLTXV2Service struct{}
func (p *ZLTXV2Service) 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 *ZLTXV2Service) Order(ctx context.Context, request *proto.OrderRequest) (*proto.OrderResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
client, err := c.client()
if err != nil {
return nil, err
}
response, errResp, err := api.OrderCreate(ctx, client, c.AppId, c.orderReq(request))
if err != nil {
return nil, proto.ErrorRequestFail(fmt.Sprintf("请求异常[%v]", err))
return nil, fmt.Errorf("请求异常[%v]", err)
}
if errResp != nil {
return nil, proto.ErrorRequestFail(fmt.Sprintf("请求错误[code:%d]-[reason:%s]-[msg:%s]", errResp.Code, errResp.Reason, errResp.Message))
return nil, fmt.Errorf("请求错误[code:%d]-[reason:%s]-[msg:%s]", errResp.Code, errResp.Reason, errResp.Message)
}
return orderResp(request, response), nil
}
func (p *ZLTXV2Service) 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 *ZLTXV2Service) Query(ctx context.Context, request *proto.QueryRequest) (*proto.QueryResponse, error) {
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
client, err := c.client()
if err != nil {
return nil, err
@ -72,42 +55,39 @@ func (p *ZLTXV2Service) Query(ctx context.Context, request *proto.QueryRequest)
response, errResp, err := api.OrderQuery(ctx, client, c.AppId, c.queryReq(request))
if err != nil {
return nil, proto.ErrorRequestFail(fmt.Sprintf("请求异常[%v]", err))
return nil, fmt.Errorf("请求异常[%v]", err)
}
if errResp != nil {
return nil, proto.ErrorRequestFail(fmt.Sprintf("请求错误[code:%d]-[reason:%s]-[msg:%s]", errResp.Code, errResp.Reason, errResp.Message))
return nil, fmt.Errorf("请求错误[code:%d]-[reason:%s]-[msg:%s]", errResp.Code, errResp.Reason, errResp.Message)
}
return queryResp(c.AppKey, request, response)
}
func (p *ZLTXV2Service) 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 (p *ZLTXV2Service) Notify(_ context.Context, request *proto.NotifyRequest) (*proto.NotifyResponse, error) {
req := &http.Request{
Header: nil,
Body: nil,
}
httpHeaders := make(http.Header)
if err := json.Unmarshal(request.Headers, &httpHeaders); err != nil {
return nil, proto.ErrorParamFail(fmt.Sprintf("headers Unmarshal err [%v]", err))
return nil, fmt.Errorf("headers Unmarshal err [%v]", err)
}
req := &http.Request{
Header: httpHeaders,
Body: io.NopCloser(bytes.NewBuffer(request.Body)),
newHeaders := make(http.Header)
for key, values := range httpHeaders {
newKey := strings.ToTitle(strings.ToLower(key))
newHeaders[newKey] = values
}
req.Header = newHeaders
req.Body = io.NopCloser(bytes.NewBuffer(request.Body))
c, err := transConfig(request.Config)
if err != nil {
return nil, err
}
n, err := notify.NewNotify(c.AppId, c.AppKey).ParseAndVerify(req)
if err != nil {
return nil, proto.ErrorSignFail(err.Error())
return nil, err
}
return notifyResp(c.AppKey, n)

View File

@ -6,7 +6,7 @@ import (
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/stretchr/testify/assert"
"net/http"
"google.golang.org/grpc/metadata"
"testing"
)
@ -14,13 +14,12 @@ var zltx = &ZLTXV2Service{}
func config() []byte {
c := &Config{
AppId: "25891",
AppKey: "83cc38e09560417ad7ea0feaaae9d171",
//BaseUri: "http://211.137.105.198:17100",
BaseUri: "http://117.175.169.61:17100",
NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify",
AppId: "23329",
AppKey: "8db16e8cc8363ed4eb4c14f9520bcc32",
BaseUri: "http://211.137.105.198:17100",
//NotifyUrl: "https://utils.85938.cn/utils/v1/wechat/notify",
NotifyUrl: "http://120.55.12.245:8098/v1/order/direct/notify",
}
//{"app_id":"25891","app_key":"83cc38e09560417ad7ea0feaaae9d171","merchant_id":25891}
marshal, _ := json.Marshal(c)
return marshal
}
@ -37,8 +36,8 @@ func TestOrder(t *testing.T) {
request := &proto.OrderRequest{
Config: config(),
Order: &proto.OrderRequest_Order{
OrderNo: "test_zltx_v2_8",
Account: "18666666666",
OrderNo: "test_zltx_v2_6",
Account: "18666173766",
Quantity: 1,
Extra: nil,
},
@ -63,8 +62,8 @@ func TestQuery(t *testing.T) {
request := &proto.QueryRequest{
Config: config(),
Order: &proto.QueryRequest_Order{
OrderNo: "test_zltx_v2_7",
TradeNo: "", // 731095414466813953
OrderNo: "test_zltx_v2_3",
TradeNo: "",
Account: "",
Extra: nil,
},
@ -81,29 +80,42 @@ func TestQuery(t *testing.T) {
}
func TestNotify(t *testing.T) {
bodyBytes := []byte(`{"tradeStatus":"SUCCESS","orderNo":"C2169720720874659840","tradeStateDesc":"成功","cards":[{"no":"RpHWw3cEhiRW764pFoKVHKRypwZg8Txf0YAW8dE+F7lsrkoUKaAlkYL549lRWp+Q","pwd":"","url":"hei8pdKAWO7aiFHPmtZWg1iTupwk3oXV8QH7IVBtUpoPhFt6wksIiN034wbbe9sOo8Wpl7iOXUVSd50mJdwDknUi8BpitNpODfjjiTt7vTk=","deadline":"2025-09-30 00:00:00","cardType":3}],"mchId":25891,"outTradeNo":"202504031355308910010048","rechargeAccount":"","attach":"","unitPrice":27}`)
headerBytes := []byte(`{"Accept-Encoding":["gzip"],"Authorization":["a4decf2803323a023d187578f85f4d21"],"Connection":["close"],"Content-Length":["442"],"Content-Type":["application/json"],"Lsxd-Api-Proxy":["v2"],"Lsxd-From-System":["card"],"Lsxd-Signature":["bc0eb734101796c5bd1fe72472b406a4"],"Lsxd-Timestamp":["1743659731"],"User-Agent":["Go-http-client/1.1"],"X-Envoy-Attempt-Count":["1"],"X-Forwarded-Client-Cert":["By=spiffe://cluster.local/ns/gopro/sa/default;Hash=4798784d2ecd4218a4b71b4d08737f9714e2dbd508446d916b2999fd22d5811c;Subject=\"\";URI=spiffe://cluster.local/ns/card/sa/default"],"X-Forwarded-For":["112.124.3.46"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["112.124.3.46"],"X-Remoteaddr":["172.18.0.1"],"X-Request-Id":["eab52927-95c8-4e64-9f5c-8a5f2aa339c1"]}`)
jsonData := []byte(`{
"tradeStatus": "SUCCESS",
"orderNo": "C2081410801687044096",
"tradeStateDesc": "成功",
"cards": [
{"no":"*","pwd":"*","deadline":"2024-07-03 23:59:59","cardType":1}
],
"mchId": 23329,
"outTradeNo": "20240802212326527830",
"rechargeAccount": "17384082748",
"unitPrice":102.5
}`)
md := metadata.New(map[string]string{
"Authorization": "292e21f3683219369cf68dede0d45730",
"Content-Type": "application/json",
})
ctx := metadata.NewIncomingContext(context.Background(), md)
headerData, ok := metadata.FromIncomingContext(ctx)
if !ok {
t.Error("无法获取 Header 数据")
return
}
HeaderBytes, _ := json.Marshal(headerData)
in := &proto.NotifyRequest{
Config: config(),
Queries: nil,
Headers: headerBytes,
Body: bodyBytes,
Headers: HeaderBytes,
Body: jsonData,
}
t.Run("TestNotify", func(t *testing.T) {
got, err := zltx.Notify(context.Background(), in)
got, err := zltx.Notify(ctx, in)
if !assert.Nil(t, err) {
t.Errorf("Notify() error = %v", err)
return
}
responseHeaders := make(http.Header)
_ = json.Unmarshal([]byte(got.Headers), &responseHeaders)
for s, strings := range responseHeaders {
t.Logf("%s: %v", s, strings)
}
t.Logf("%s \n", got.String())
fmt.Printf("%s \n", got.String())
assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
})
}

View File

@ -44,41 +44,34 @@ func GetCert(appId string) (*CertConfig, error) {
if c != nil {
return c, nil
}
dir, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("get current dir error: %v", err)
}
filePath := fmt.Sprintf("%s/%s/%s/%s", dir, "cert", "alipay", appId)
if !helper.FileExists(filePath) {
return nil, fmt.Errorf("appId[%s][%s]支付宝密钥文件信息不存在,请联系技术人员处理", appId, filePath)
return nil, fmt.Errorf("appId[%s]支付宝密钥文件信息不存在,请联系技术人员处理", appId)
}
mchCertPath := fmt.Sprintf("%s/%s_%s.crt", filePath, "appCertPublicKey", appId)
mchCertSN, err := getMchCertSN(mchCertPath)
if err != nil {
return nil, fmt.Errorf("get mchCertSN error: %v", err)
}
rootCertPath := fmt.Sprintf("%s/%s", filePath, "alipayRootCert.crt")
rootCertSN, err := getRootCertSN(rootCertPath)
if err != nil {
return nil, fmt.Errorf("get rootCertSN error: %v", err)
}
publicKeyPath := fmt.Sprintf("%s/%s", filePath, "alipayCertPublicKey_RSA2.crt")
publicKey, err := getPublicKey(publicKeyPath)
if err != nil {
return nil, fmt.Errorf("get publicKey error: %v", err)
}
c = &CertConfig{
MchCertSN: mchCertSN,
RootCertSN: rootCertSN,
PublicKey: publicKey,
}
setCertConfig(appId, c)
return c, nil
}

View File

@ -3,13 +3,14 @@ package alipay
import (
"fmt"
"io/ioutil"
"log"
)
// getMchCertSN 提取证书序列号
func getMchCertSN(certPath string) (string, error) {
certData, err := ioutil.ReadFile(certPath)
if err != nil {
return "", fmt.Errorf("Failed to read the cert file: %s", err)
log.Fatalf("Failed to read the cert file: %s", err)
}
cert, err := getCert(certData)

View File

@ -3,7 +3,7 @@ module plugins/utils
go 1.22.2
require (
gitea.cdlsxd.cn/sdk/plugin v1.0.17
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240830100334-2e99a5df732b
github.com/go-playground/validator/v10 v10.22.0
github.com/tjfoc/gmsm v1.4.1
github.com/wechatpay-apiv3/wechatpay-go v0.2.18

View File

@ -1,6 +1,6 @@
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 v0.0.0-20240830100334-2e99a5df732b h1:x8Gf1EJ6oLHEIgK/SilgMZ5EDgcEknef9zhGrFvXXMg=
gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240830100334-2e99a5df732b/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=

View File

@ -33,8 +33,7 @@ func IsEmail(email string) bool {
// IsValidQQ 检查给定的字符串是否为有效的 QQ 号
func IsValidQQ(qq string) bool {
// QQ号正则表达式5到11位数字且开头不为0的情况,
regex := `^[1-9][0-9]{4,11}$`
//re := regexp.MustCompile(`^[1-9]\d{4,10}$`)
return regexp.MustCompile(regex).MatchString(qq)
// QQ号正则表达式5到11位数字且开头不为0的情况
re := regexp.MustCompile(`^(?!0)[0-9]{5,11}$`)
return re.MatchString(qq)
}

View File

@ -7,9 +7,3 @@ func TestToChinese(t *testing.T) {
got := ToChinese(arg)
t.Log(got)
}
func TestAccountType(t *testing.T) {
arg := "3122222222"
got := IsValidQQ(arg)
t.Log(got)
}

View File

@ -9,90 +9,30 @@ import (
"time"
)
type Options struct {
Headers http.Header
StatusCodeFunc func(int) bool
Timeout time.Duration
}
func NewOptions(options ...Option) *Options {
o := &Options{
Headers: http.Header{
"Content-Type": []string{"application/json"},
},
StatusCodeFunc: func(code int) bool {
return code == http.StatusOK
},
func Request(_ context.Context, method, url string, body []byte) ([]byte, http.Header, error) {
req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
if err != nil {
return nil, nil, err
}
httpClient := &http.Client{
Timeout: 15 * time.Second,
}
for _, option := range options {
option(o)
}
return o
}
type Option func(*Options)
func WithTimeout(timeout time.Duration) Option {
return func(options *Options) {
options.Timeout = timeout
}
}
func WithHeaders(headers http.Header) Option {
return func(options *Options) {
options.Headers = headers
}
}
func WithStatusCodeFunc(statusCodeFunc func(int) bool) Option {
return func(options *Options) {
options.StatusCodeFunc = statusCodeFunc
}
}
func Post(ctx context.Context, url string, body []byte, options ...Option) (http.Header, []byte, error) {
return Request(ctx, http.MethodPost, url, body, NewOptions(options...))
}
func Get(ctx context.Context, url string, options ...Option) (http.Header, []byte, error) {
return Request(ctx, http.MethodGet, url, nil, NewOptions(options...))
}
func Put(ctx context.Context, url string, body []byte, options ...Option) (http.Header, []byte, error) {
return Request(ctx, http.MethodPut, url, body, NewOptions(options...))
}
func Request(_ context.Context, method, url string, body []byte, o *Options) (http.Header, []byte, error) {
req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
if err != nil {
return nil, nil, fmt.Errorf("创建HTTP请求失败: %w", err)
}
req.Header = o.Headers
httpClient := &http.Client{
Timeout: o.Timeout,
}
resp, err := httpClient.Do(req)
if err != nil {
return nil, nil, fmt.Errorf("发送HTTP请求失败: %w", err)
return nil, nil, err
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, nil, fmt.Errorf("读取响应体失败: %w", err)
return nil, nil, err
}
if !o.StatusCodeFunc(resp.StatusCode) {
if resp.StatusCode != http.StatusOK {
return nil, nil, fmt.Errorf("请求异常:%s", resp.Status)
}
return resp.Header, bodyBytes, nil
return bodyBytes, resp.Header, nil
}

View File

@ -1,66 +0,0 @@
package request
import (
"context"
"net/http"
"net/url"
"testing"
"time"
)
func Test_Get(t *testing.T) {
uri := "https://gateway.dev.cdlsxd.cn/adminyx/admin/v1/key_batch/list"
uv := url.Values{}
uv.Set("page", "1")
uv.Set("limit", "2")
h := http.Header{
"Content-Type": []string{"application/x-www-form-urlencoded"},
}
respHeader, respBody, err := Get(context.Background(), uri+"?"+uv.Encode(), WithHeaders(h))
if err != nil {
t.Error(err)
return
}
t.Logf("响应体:", string(respBody))
t.Logf("响应头:", respHeader)
}
func Test_RequestHeaders(t *testing.T) {
uri := "http://example.com/api"
body := []byte("request body")
h := http.Header{
"Content-Type": []string{"application/json"},
"Authorization": []string{"Bearer token"},
}
respHeader, respBody, err := Post(context.Background(), uri, body, WithTimeout(10*time.Second), WithHeaders(h))
if err != nil {
t.Error(err)
return
}
t.Logf("响应体:", string(respBody))
t.Logf("响应头:", respHeader)
}
func Test_RequestStatusCode(t *testing.T) {
uri := "http://example.com/api/update"
body := []byte("update data")
isSuccess := func(code int) bool {
return code == http.StatusOK || code == http.StatusCreated
}
respHeader, respBody, err := Put(context.Background(), uri, body, WithStatusCodeFunc(isSuccess))
if err != nil {
t.Error(err)
return
}
t.Logf("响应体:", string(respBody))
t.Logf("响应头:", respHeader)
}

View File

@ -4,7 +4,6 @@ import (
"context"
"crypto/x509"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/go-playground/validator/v10"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
@ -22,7 +21,7 @@ func (c *Server) Validate() error {
err := validator.New().Struct(c)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return proto.ErrorConfigFail("配置有误:" + err.Error())
return fmt.Errorf("配置有误:" + err.Error())
}
}
return nil
@ -31,27 +30,23 @@ func (c *Server) Validate() error {
func newClient(ctx context.Context, c *Server) (*core.Client, error) {
dir, err := os.Getwd()
if err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("MchID[%s] get current dir error:%v", c.MchID, err))
return nil, fmt.Errorf("MchID[%s] get current dir error:%v", c.MchID, err)
}
filePath := fmt.Sprintf("%s/%s/%s/%s", dir, "cert", "wechat", c.MchID)
if !putils.FileExists(filePath) {
return nil, proto.ErrorConfigFail(fmt.Sprintf("MchID[%s]微信密钥证书信息不存在,请联系技术人员处理", c.MchID))
return nil, fmt.Errorf("MchID[%s]微信密钥证书信息不存在,请联系技术人员处理", c.MchID)
}
// 商户相关配置,商户API私钥
mchPrivateKey, err := utils.LoadPrivateKeyWithPath(fmt.Sprintf("%s/%s", filePath, "wechat_private_key.pem"))
if err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("MchID[%s] load merchant private key error:%v", c.MchID, err))
return nil, fmt.Errorf("MchID[%s] load merchant private key error:%v", c.MchID, err)
}
var client *core.Client
// 微信支付平台配置
wechatPayCertificate, err := utils.LoadCertificateWithPath(fmt.Sprintf("%s/%s", filePath, "wechat_cert.pem"))
if err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("平台证书有误:%v", err))
return nil, fmt.Errorf("平台证书有误:%v", err)
}
client, err = core.NewClient(
ctx,
option.WithMerchantCredential(
@ -62,8 +57,7 @@ func newClient(ctx context.Context, c *Server) (*core.Client, error) {
option.WithWechatPayCertificate([]*x509.Certificate{wechatPayCertificate}),
)
if err != nil {
return nil, proto.ErrorConfigFail(fmt.Sprintf("创建 Client 失败:%v", err))
return nil, fmt.Errorf("创建 Client 失败:%v", err)
}
return client, nil
}

View File

@ -2,7 +2,6 @@ package wechat
import (
"context"
"gitea.cdlsxd.cn/sdk/plugin/proto"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/auth"
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
@ -26,30 +25,21 @@ func init() {
func GetClient(ctx context.Context, c *Server) (*core.Client, error) {
instance.mutex.Lock()
defer instance.mutex.Unlock()
if instance.clients[c.MchID] != nil {
return instance.clients[c.MchID], nil
}
var err error
instance.once.Do(func() {
instance.clients[c.MchID], err = newClient(ctx, c)
})
if err != nil {
return nil, err
}
return instance.clients[c.MchID], nil
}
func (s *Server) Verify(ctx context.Context, message, signature string) error {
var verifier auth.Verifier
verifier = verifiers.NewSHA256WithRSAVerifier(nil)
if err := verifier.Verify(ctx, s.MchCertificateSerialNumber, message, signature); err != nil {
return proto.ErrorSignFail(err.Error())
}
return nil
return verifier.Verify(ctx, s.MchCertificateSerialNumber, message, signature)
}