Compare commits

..

23 Commits
v1 ... main

Author SHA1 Message Date
李子铭 e0b1bf4b43 卡密,直充,回调 2024-11-15 16:45:50 +08:00
李子铭 84a6cb208f 卡密,直充,回调 2024-11-15 16:40:51 +08:00
李子铭 b71aa93e65 卡密,直充,优化 2024-11-15 16:15:54 +08:00
李子铭 85034cfb35 卡密,直充,Request调整 2024-11-15 16:08:35 +08:00
李子铭 50ab391039 卡密,直充,订单结果msg处理 2024-11-15 15:03:52 +08:00
李子铭 fe8e56420f 卡密,直充 2024-11-15 14:51:11 +08:00
李子铭 35ccf00466 order 2024-11-15 14:47:01 +08:00
李子铭 8102c3ffcd CardCode 2024-11-15 14:29:41 +08:00
李子铭 eb9a1323e5 CardCode 2024-11-15 14:27:58 +08:00
李子铭 1b3162df5d TestZc_DctWServer 2024-11-15 13:47:51 +08:00
李子铭 990030fff3 TestZc_DctWServer 2024-11-15 11:00:16 +08:00
李子铭 0823da7786 Md5 ToUpper 2024-11-15 10:57:58 +08:00
李子铭 3069c47115 test 2024-11-15 10:41:03 +08:00
李子铭 5e71fabbf9 test 2024-11-15 10:34:36 +08:00
李子铭 e0985d04af test 2024-11-14 19:25:13 +08:00
李子铭 c9c91d9146 直连天下,sdk开发 2024-11-14 18:33:56 +08:00
李子铭 307205cdfd return 2024-11-07 10:40:35 +08:00
李子铭 0ea434699a manage 2024-11-06 13:37:23 +08:00
李子铭 7cc22209f5 int 2024-10-23 16:51:06 +08:00
李子铭 aa83189b01 int 2024-10-23 16:49:40 +08:00
李子铭 48907c4653 getCert 从证书数据中提取证书 2024-10-23 15:53:21 +08:00
李子铭 a241f05370 change sdk 2024-10-23 15:45:51 +08:00
李子铭 83c54628dd change sdk 2024-10-23 15:39:51 +08:00
29 changed files with 1279 additions and 126 deletions

View File

@ -13,13 +13,14 @@ func TestToken_Client(t *testing.T) {
MchCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt", MchCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt",
RootCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt", RootCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt",
} }
resp, err := d.Client(context.Background(), "349feb5fb58e455da1b00a7748ceMD68") resp, err := d.Client(context.Background(), "ecfb8b3f91e44ca6b94b8830fcd5BF88")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} else { } else {
if resp.IsSuccess() { if resp.IsSuccess() {
t.Logf("Response: %+v", resp.Response) t.Logf("Response: %+v", resp.Response)
} else { } else {
t.Errorf("resp: %+v", resp)
t.Errorf("ErrorResponse: %+v", resp.ErrorResponse) t.Errorf("ErrorResponse: %+v", resp.ErrorResponse)
t.Errorf("ErrorResponse Msg: %+v", resp.ErrorResponse.Msg) t.Errorf("ErrorResponse Msg: %+v", resp.ErrorResponse.Msg)
t.Errorf("ErrorResponse SubCode: %+v", resp.ErrorResponse.SubCode) t.Errorf("ErrorResponse SubCode: %+v", resp.ErrorResponse.SubCode)

View File

@ -22,19 +22,20 @@ type Param struct {
GrantType string `json:"grant_type"` GrantType string `json:"grant_type"`
Code string `json:"code"` Code string `json:"code"`
} }
type AlipayTokenSuccessResponse struct { type AlipayTokenSuccessResponse struct {
UserId string `json:"user_id"` UserId string `json:"user_id"`
OpenId string `json:"open_id"` OpenId string `json:"open_id"`
AccessToken string `json:"access_token"` AccessToken string `json:"access_token"`
ExpiresIn string `json:"expires_in"` ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
ReExpiresIn string `json:"re_expires_in"` ReExpiresIn int `json:"re_expires_in"`
AuthStart string `json:"auth_start"` AuthStart string `json:"auth_start"`
} }
type AlipayTokenResponse struct { type AlipayTokenResponse struct {
ErrorResponse *alipay.ErrorResponse `json:"error_response"` ErrorResponse *alipay.ErrorResponse `json:"error_response,omitempty"`
Response *AlipayTokenSuccessResponse `json:"alipay_system_oauth_token_response"` Response *AlipayTokenSuccessResponse `json:"alipay_system_oauth_token_response,omitempty"`
Sign string `json:"sign"` Sign string `json:"sign"`
} }

View File

@ -43,7 +43,7 @@ func GetCert(mchCertPath, rootCertPath, PublicKeyPath, appId string) (*CertConfi
} }
c := getCertConfig(appId) c := getCertConfig(appId)
if c != nil { if c != nil {
return nil, fmt.Errorf("appId %s already exists", appId) return c, nil
} }
mchCertSN, err := getMchCertSN(mchCertPath) mchCertSN, err := getMchCertSN(mchCertPath)
if err != nil { if err != nil {

View File

@ -23,6 +23,7 @@ func hex2dec(hex string) string {
return fmt.Sprintf("%d", dec) return fmt.Sprintf("%d", dec)
} }
// getCert 从证书数据中提取证书
func getCert(certData []byte) (*x509.Certificate, error) { func getCert(certData []byte) (*x509.Certificate, error) {
block, _ := pem.Decode(certData) block, _ := pem.Decode(certData)
if block == nil { if block == nil {

65
dctw/v1/api/card/card.go Normal file
View File

@ -0,0 +1,65 @@
package card
import (
"context"
"encoding/json"
"gitea.cdlsxd.cn/sdk/plugin/dctw/v1/api"
"gitea.cdlsxd.cn/sdk/plugin/dctw/v1/core"
"io/ioutil"
"net/http"
)
type Card api.Service
const (
orderPath = "/internal/v1/card/order"
queryPath = "/internal/v1/card/query"
)
func (c *Card) Order(ctx context.Context, request *Order) (*OrderResp, error) {
result, err := c.Request(ctx, request, orderPath)
if err != nil {
return nil, err
}
var response *OrderResp
if err = json.Unmarshal(result, &response); err != nil {
return nil, err
}
return response, nil
}
func (c *Card) Query(ctx context.Context, request *Query) (*QueryResp, *CardCode, error) {
result, err := c.Request(ctx, request, queryPath)
if err != nil {
return nil, nil, err
}
var response *QueryResp
if err = json.Unmarshal(result, &response); err != nil {
return nil, nil, err
}
cardCode, err := response.Decode(c.Config.AppKey)
return response, cardCode, nil
}
func (c *Card) Notify(_ context.Context, httpRequest *http.Request) (*Notify, *CardCode, error) {
body, err := ioutil.ReadAll(httpRequest.Body)
if err != nil {
return nil, nil, err
}
if err = c.VerifyDctW(httpRequest.Header.Get(core.SignKeyName), body); err != nil {
return nil, nil, err
}
var response *Notify
if err = json.Unmarshal(body, &response); err != nil {
return nil, nil, err
}
cardCode, err := response.Decode(c.Config.AppKey)
return response, cardCode, err
}

View File

@ -0,0 +1,108 @@
package card
import (
"bytes"
"context"
"encoding/json"
"gitea.cdlsxd.cn/sdk/plugin/dctw/v1/core"
"github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"testing"
)
func TestCard_Order(t *testing.T) {
server, err := core.NewDctWServer(
&core.DctWConfig{
AppId: "1",
AppKey: "1e2bf7a04b8b1e6be5dc78d04e8639c9",
BaseUri: "http://test.openapi.1688sup.cn",
},
core.WithDebug(true),
)
if err != nil {
t.Fatal(err)
}
a := &Card{DctWServer: server}
req := &Order{
Number: 1,
MerchantId: "23329",
OutTradeNo: "test_card_zltx_2",
ProductId: "222",
Mobile: "18666666666",
NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify",
Version: "1.0",
}
resp, err := a.Order(context.Background(), req)
if err != nil {
t.Error(err)
return
}
if !resp.GetCode().IsSuccess() {
t.Error(resp.Message)
return
}
t.Logf("%+v", resp)
}
func TestCard_Query(t *testing.T) {
server, err := core.NewDctWServer(&core.DctWConfig{
AppId: "1",
AppKey: "1e2bf7a04b8b1e6be5dc78d04e8639c9",
BaseUri: "http://test.openapi.1688sup.cn",
})
if err != nil {
t.Fatal(err)
}
a := &Card{DctWServer: server}
req := &Query{
MerchantId: "23329",
OutTradeNo: "test_card_zltx_2",
Version: "1.0",
}
resp, cardCode, err := a.Query(context.Background(), req)
if err != nil {
t.Error(err)
return
}
if !resp.GetCode().IsSuccess() {
t.Error(resp.Message)
return
}
t.Logf("%+v", resp)
t.Logf("%+v", cardCode)
assert.Equal(t, resp.Status.IsSuccess(), true, "充值是否成功")
}
func TestCard_Notify(t *testing.T) {
server, err := core.NewDctWServer(&core.DctWConfig{
AppId: "1",
AppKey: "1e2bf7a04b8b1e6be5dc78d04e8639c9",
BaseUri: "http://test.openapi.1688sup.cn",
})
if err != nil {
t.Fatal(err)
}
a := &Card{DctWServer: server}
body := []byte(`{"merchantId":"23329","outTradeNo":"test_card_zltx_2","status":"01"}`)
httpHeaderBytes := []byte(`{"Accept-Encoding":["gzip, deflate, br"],"Authorization":["MD5 appid=1,sign=6B48E95EAEE20EE4525BEF0112493711"],"Connection":["close"],"Content-Length":["68"],"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"]}`)
httpHeaders := make(http.Header)
if err = json.Unmarshal(httpHeaderBytes, &httpHeaders); err != nil {
t.Fatal(err)
}
var httpRequest = &http.Request{
Header: httpHeaders,
Body: ioutil.NopCloser(bytes.NewBuffer(body)),
}
resp, cardCode, err := a.Notify(context.Background(), httpRequest)
if err != nil {
t.Error(err)
return
}
t.Logf("%+v", resp)
t.Logf("%+v", cardCode)
assert.Equal(t, resp.Status.IsSuccess(), true, "充值是否成功")
}

40
dctw/v1/api/card/code.go Normal file
View File

@ -0,0 +1,40 @@
package card
import "encoding/json"
type Code json.Number
const (
CodeSuccess Code = "2000"
CodeSuccess2 Code = "0000"
)
var ResMessageMap = map[Code]string{
CodeSuccess: "成功",
"1000": "Ip limit(ip未绑定或绑定失败)",
"1001": "Missing parameters(参数异常)",
"1002": "Invalid merchant(无效商户信息)",
"1003": "Invalid signature(签名校验失败)",
"1004": "Request expiration(请求时间过期)",
"1005": "Order repeat(订单重复)",
"1006": "Invalid item(商品未开通)",
"1007": "Item price invalid(商品价格无效)",
"1008": "Insufficient Balance(余额不足)",
"1009": "Interface adjustment(商品映射无效)",
"1010": "Interface price adjustment(映射价格无效)",
"1011": "Account format matching(充值账号格式不匹配)",
"1012": "no order(无订单信息)",
"1999": "unknown error(异常错误,建议人工处理或查询订单状态)",
}
func (c Code) IsSuccess() bool {
// 根据沟通对接,两种都是请求成功
return c == CodeSuccess || c == CodeSuccess2
}
func (c Code) GetMessage() string {
if message, ok := ResMessageMap[c]; ok {
return message
}
return "未知错误,请联系平台"
}

View File

@ -0,0 +1,57 @@
package card
import (
"gitea.cdlsxd.cn/sdk/plugin/proto"
)
type OrderStatus string
const (
OrderSuccess OrderStatus = "01" // 充值成功
OrderPending OrderStatus = "02" // 充值处理中
OrderFail OrderStatus = "03" // 充值失败
OrderAbnormal OrderStatus = "04" // 充值异常,处理中
)
var orderStatusMap = map[OrderStatus]proto.Status{
OrderSuccess: proto.Status_SUCCESS,
OrderPending: proto.Status_ING,
OrderFail: proto.Status_FAIL,
}
var orderStatusTextMap = map[OrderStatus]string{
OrderSuccess: "充值成功",
OrderPending: "充值处理中",
OrderFail: "充值失败",
OrderAbnormal: "充值异常,处理中",
}
func (o OrderStatus) IsSuccess() bool {
return o == OrderSuccess
}
func (o OrderStatus) IsPending() bool {
return o == OrderPending
}
func (o OrderStatus) IsFail() bool {
return o == OrderFail
}
func (o OrderStatus) IsAbnormal() bool {
return o == OrderAbnormal
}
func (o OrderStatus) GetOrderStatus() proto.Status {
if resultStatus, ok := orderStatusMap[o]; ok {
return resultStatus
}
return proto.Status_ING
}
func (o OrderStatus) GetOrderStatusText() string {
if text, ok := orderStatusTextMap[o]; ok {
return text
}
return ""
}

151
dctw/v1/api/card/trans.go Normal file
View File

@ -0,0 +1,151 @@
package card
import (
"encoding/json"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/utils"
"github.com/go-playground/validator/v10"
"strings"
)
type Order struct {
Number uint32 `validate:"required" json:"number"`
MerchantId string `validate:"required" json:"merchantId"`
OutTradeNo string `validate:"required" json:"outTradeNo"`
ProductId string `validate:"required" json:"productId"`
Mobile string `json:"mobile"`
NotifyUrl string `validate:"required" json:"notifyUrl"`
Version string `validate:"required" json:"version"`
}
type OrderResp struct {
Code json.Number `json:"code"` // 不能直接定义值对象因为单独定义类型type code json.Number类型无法jsonUnmarshal
Message string `json:"message"`
TradeNo string `json:"tradeNo"`
}
type Query struct {
MerchantId string `validate:"required" json:"merchantId"`
OutTradeNo string `validate:"required" json:"outTradeNo"`
Version string `validate:"required" json:"version"`
}
type QueryResp struct {
Code json.Number `json:"code"`
Status OrderStatus `json:"status"`
Message string `json:"message"`
OutTradeNo string `json:"outTradeNo"`
CardCode string `json:"cardCode"`
}
type Notify struct {
MerchantId string `json:"merchantId"`
OutTradeNo string `json:"outTradeNo"`
TradeNo string `json:"tradeNo"`
RechargeAccount string `json:"rechargeAccount"`
Status OrderStatus `json:"status"`
CardCode string `json:"cardCode"`
}
type CardCode struct {
Number string `json:"number"`
Password string `json:"password"`
}
func (o *Order) Json() ([]byte, error) {
b, err := json.Marshal(o)
if err != nil {
return nil, err
}
return b, nil
}
func (o *Order) Validate() error {
err := validator.New().Struct(o)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
func (o *Query) Json() ([]byte, error) {
b, err := json.Marshal(o)
if err != nil {
return nil, err
}
return b, nil
}
func (o *Query) Validate() error {
err := validator.New().Struct(o)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
func (o *Notify) Json() ([]byte, error) {
b, err := json.Marshal(o)
if err != nil {
return nil, err
}
return b, nil
}
func (o *Notify) Validate() error {
err := validator.New().Struct(o)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
func (o *Notify) Decode(appKey string) (*CardCode, error) {
car := &CardCode{}
return car.Decode(o.CardCode, appKey)
}
func (o *CardCode) Json() ([]byte, error) {
b, err := json.Marshal(o)
if err != nil {
return nil, err
}
return b, nil
}
func (o *CardCode) Decode(carCode, appKey string) (*CardCode, error) {
if carCode == "" {
return o, nil
}
s, err := utils.AesDecode(carCode, []byte(appKey))
if err != nil {
return o, nil
}
parts := strings.Split(s, "_")
if len(parts) > 1 {
o.Number = parts[0]
o.Password = parts[1]
} else {
o.Password = s
}
return o, err
}
func (o *OrderResp) GetCode() Code {
return Code(o.Code)
}
func (o *QueryResp) GetCode() Code {
return Code(o.Code)
}
func (o *QueryResp) Decode(appKey string) (*CardCode, error) {
car := &CardCode{}
return car.Decode(o.CardCode, appKey)
}

View File

@ -0,0 +1,31 @@
package direct
import (
"gitea.cdlsxd.cn/sdk/plugin/utils"
)
type AccountType int
const (
AccountTypeDefault AccountType = iota
AccountTypePhone
AccountTypeQQ
)
// AccountType 账号类型(1:手机号 2:QQ号 其他0)
func (o AccountType) AccountType(account string) AccountType {
if account == "" {
return AccountTypeDefault
} else if utils.IsPhoneNumber(account) {
return AccountTypePhone
} else if utils.IsValidQQ(account) {
return AccountTypeQQ
} else if utils.IsEmail(account) {
return AccountTypeDefault
}
return AccountTypeDefault
}
func (o AccountType) Value() int {
return int(o)
}

View File

@ -0,0 +1,40 @@
package direct
import "encoding/json"
type Code json.Number
const (
CodeSuccess Code = "2000"
CodeSuccess2 Code = "0000"
)
var ResMessageMap = map[Code]string{
CodeSuccess: "成功",
"1000": "Ip limit(ip未绑定或绑定失败)",
"1001": "Missing parameters(参数异常)",
"1002": "Invalid merchant(无效商户信息)",
"1003": "Invalid signature(签名校验失败)",
"1004": "Request expiration(请求时间过期)",
"1005": "Order repeat(订单重复)",
"1006": "Invalid item(商品未开通)",
"1007": "Item price invalid(商品价格无效)",
"1008": "Insufficient Balance(余额不足)",
"1009": "Interface adjustment(商品映射无效)",
"1010": "Interface price adjustment(映射价格无效)",
"1011": "Account format matching(充值账号格式不匹配)",
"1012": "no order(无订单信息)",
"1999": "unknown error(异常错误,建议人工处理或查询订单状态)",
}
func (c Code) IsSuccess() bool {
// 根据沟通对接,两种都是请求成功
return c == CodeSuccess || c == CodeSuccess2
}
func (c Code) GetMessage() string {
if message, ok := ResMessageMap[c]; ok {
return message
}
return "未知错误,请联系平台"
}

View File

@ -0,0 +1,63 @@
package direct
import (
"context"
"encoding/json"
"gitea.cdlsxd.cn/sdk/plugin/dctw/v1/api"
"gitea.cdlsxd.cn/sdk/plugin/dctw/v1/core"
"io/ioutil"
"net/http"
)
type Direct api.Service
const (
orderPath = "/internal/v1/recharge/create"
queryPath = "/internal/v1/recharge/query"
)
func (c *Direct) Order(ctx context.Context, request *Order) (*OrderResp, error) {
result, err := c.Request(ctx, request, orderPath)
if err != nil {
return nil, err
}
var response *OrderResp
if err = json.Unmarshal(result, &response); err != nil {
return nil, err
}
return response, nil
}
func (c *Direct) Query(ctx context.Context, request *Query) (*QueryResp, error) {
result, err := c.Request(ctx, request, queryPath)
if err != nil {
return nil, err
}
var response *QueryResp
if err = json.Unmarshal(result, &response); err != nil {
return nil, err
}
return response, nil
}
func (c *Direct) Notify(_ context.Context, httpRequest *http.Request) (*Notify, error) {
body, err := ioutil.ReadAll(httpRequest.Body)
if err != nil {
return nil, err
}
if err = c.VerifyDctW(httpRequest.Header.Get(core.SignKeyName), body); err != nil {
return nil, err
}
var response *Notify
if err = json.Unmarshal(body, &response); err != nil {
return nil, err
}
return response, err
}

View File

@ -0,0 +1,107 @@
package direct
import (
"bytes"
"context"
"encoding/json"
"gitea.cdlsxd.cn/sdk/plugin/dctw/v1/core"
"github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"testing"
)
func TestDirect_Order(t *testing.T) {
server, err := core.NewDctWServer(
&core.DctWConfig{
AppId: "1",
AppKey: "1e2bf7a04b8b1e6be5dc78d04e8639c9",
BaseUri: "http://test.openapi.1688sup.cn",
},
core.WithDebug(true),
)
if err != nil {
t.Fatal(err)
}
a := &Direct{DctWServer: server}
req := &Order{
Number: 1,
MerchantId: "25537",
OutTradeNo: "test_zltx_direct_2",
ProductId: "101",
AccountType: 1,
RechargeAccount: "18666666666",
NotifyUrl: "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify",
Version: "1.0",
}
resp, err := a.Order(context.Background(), req)
if err != nil {
t.Error(err)
return
}
if !resp.GetCode().IsSuccess() {
t.Error(resp.Message)
return
}
t.Logf("%+v", resp)
}
func TestDirect_Query(t *testing.T) {
server, err := core.NewDctWServer(&core.DctWConfig{
AppId: "1",
AppKey: "1e2bf7a04b8b1e6be5dc78d04e8639c9",
BaseUri: "http://test.openapi.1688sup.cn",
})
if err != nil {
t.Fatal(err)
}
a := &Direct{DctWServer: server}
req := &Query{
MerchantId: "25537",
OutTradeNo: "test_zltx_direct_2",
Version: "1.0",
}
resp, err := a.Query(context.Background(), req)
if err != nil {
t.Error(err)
return
}
if !resp.GetCode().IsSuccess() {
t.Error(resp.Message)
return
}
t.Logf("%+v", resp)
assert.Equal(t, resp.Status.IsSuccess(), true, "充值是否成功")
}
func TestDirect_Notify(t *testing.T) {
server, err := core.NewDctWServer(&core.DctWConfig{
AppId: "1",
AppKey: "1e2bf7a04b8b1e6be5dc78d04e8639c9",
BaseUri: "http://test.openapi.1688sup.cn",
})
if err != nil {
t.Fatal(err)
}
a := &Direct{DctWServer: server}
body := []byte(`{"merchantId":"25537","outTradeNo":"test_zltx_direct_2","status":"03","rechargeAccount":"18666666666"}`)
httpHeaderBytes := []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"]}`)
httpHeaders := make(http.Header)
if err = json.Unmarshal(httpHeaderBytes, &httpHeaders); err != nil {
t.Fatal(err)
}
var httpRequest = &http.Request{
Header: httpHeaders,
Body: ioutil.NopCloser(bytes.NewBuffer(body)),
}
resp, err := a.Notify(context.Background(), httpRequest)
if err != nil {
t.Error(err)
return
}
t.Logf("%+v", resp)
assert.Equal(t, resp.Status.IsSuccess(), true, "充值是否成功")
}

View File

@ -0,0 +1,57 @@
package direct
import (
"gitea.cdlsxd.cn/sdk/plugin/proto"
)
type OrderStatus string
const (
OrderSuccess OrderStatus = "01" // 充值成功
OrderPending OrderStatus = "02" // 充值处理中
OrderFail OrderStatus = "03" // 充值失败
OrderAbnormal OrderStatus = "04" // 充值异常,处理中
)
var orderStatusMap = map[OrderStatus]proto.Status{
OrderSuccess: proto.Status_SUCCESS,
OrderPending: proto.Status_ING,
OrderFail: proto.Status_FAIL,
}
var orderStatusTextMap = map[OrderStatus]string{
OrderSuccess: "充值成功",
OrderPending: "充值处理中",
OrderFail: "充值失败",
OrderAbnormal: "充值异常,处理中",
}
func (o OrderStatus) IsSuccess() bool {
return o == OrderSuccess
}
func (o OrderStatus) IsPending() bool {
return o == OrderPending
}
func (o OrderStatus) IsFail() bool {
return o == OrderFail
}
func (o OrderStatus) IsAbnormal() bool {
return o == OrderAbnormal
}
func (o OrderStatus) GetOrderStatus() proto.Status {
if resultStatus, ok := orderStatusMap[o]; ok {
return resultStatus
}
return proto.Status_ING
}
func (o OrderStatus) GetOrderStatusText() string {
if text, ok := orderStatusTextMap[o]; ok {
return text
}
return ""
}

108
dctw/v1/api/direct/trans.go Normal file
View File

@ -0,0 +1,108 @@
package direct
import (
"encoding/json"
"fmt"
"github.com/go-playground/validator/v10"
)
type Order struct {
Number uint32 `validate:"required" json:"number"`
MerchantId string `validate:"required" json:"merchantId"`
OutTradeNo string `validate:"required" json:"outTradeNo"`
ProductId string `validate:"required" json:"productId"`
AccountType AccountType `validate:"required" json:"accountType"`
RechargeAccount string `validate:"required" json:"rechargeAccount"`
NotifyUrl string `validate:"required" json:"notifyUrl"`
Version string `validate:"required" json:"version"`
}
type OrderResp struct {
Code json.Number `json:"code"`
Message string `json:"message"`
TradeNo string `json:"tradeNo"`
}
type Query struct {
MerchantId string `validate:"required" json:"merchantId"`
OutTradeNo string `validate:"required" json:"outTradeNo"`
Version string `validate:"required" json:"version"`
}
type QueryResp struct {
Code json.Number `json:"code"`
Status OrderStatus `json:"status"`
Message string `json:"message"`
OutTradeNo string `json:"outTradeNo"`
CardCode string `json:"cardCode"`
}
type Notify struct {
MerchantId string `json:"merchantId"`
OutTradeNo string `json:"outTradeNo"`
RechargeAccount string `json:"rechargeAccount"`
TradeNo string `json:"tradeNo"`
Status OrderStatus `json:"status"`
}
func (o *Order) Json() ([]byte, error) {
b, err := json.Marshal(o)
if err != nil {
return nil, err
}
return b, nil
}
func (o *Order) Validate() error {
err := validator.New().Struct(o)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
func (o *Query) Json() ([]byte, error) {
b, err := json.Marshal(o)
if err != nil {
return nil, err
}
return b, nil
}
func (o *Query) Validate() error {
err := validator.New().Struct(o)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
func (o *Notify) Json() ([]byte, error) {
b, err := json.Marshal(o)
if err != nil {
return nil, err
}
return b, nil
}
func (o *Notify) Validate() error {
err := validator.New().Struct(o)
if err != nil {
for _, err = range err.(validator.ValidationErrors) {
return fmt.Errorf("参数有误:" + err.Error())
}
}
return nil
}
func (o *OrderResp) GetCode() Code {
return Code(o.Code)
}
func (o *QueryResp) GetCode() Code {
return Code(o.Code)
}

7
dctw/v1/api/service.go Normal file
View File

@ -0,0 +1,7 @@
package api
import "gitea.cdlsxd.cn/sdk/plugin/dctw/v1/core"
type Service struct {
*core.DctWServer
}

193
dctw/v1/core/core.go Normal file
View File

@ -0,0 +1,193 @@
package core
import (
"bytes"
"context"
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/utils"
"github.com/go-playground/validator/v10"
"io/ioutil"
"net/http"
"strings"
"time"
)
const (
DeBug = true
SignType = "MD5"
SignKeyName = "Authorization"
ApplicationJSON = "application/json"
)
// DctWRequest 直连天下 Direct connection The world
type DctWRequest interface {
Json() ([]byte, error)
Validate() error
}
type DctWConfig struct {
AppId string `json:"app_id" validate:"required"`
AppKey string `json:"app_key" validate:"required"`
BaseUri string `json:"base_uri" validate:"required"`
}
func (c *DctWConfig) Validate() error {
if err := validator.New().Struct(c); err != nil {
for _, err = range err.(validator.ValidationErrors) {
return err
}
}
return nil
}
type DctWServer struct {
DeBug bool
Config *DctWConfig
HttpClient *http.Client
}
type Option func(*DctWServer)
func WithHttpClient(client *http.Client) Option {
return func(s *DctWServer) {
s.HttpClient = client
}
}
func WithDebug(debug bool) Option {
return func(s *DctWServer) {
s.DeBug = debug
}
}
func NewDctWServer(config *DctWConfig, o ...Option) (*DctWServer, error) {
if err := config.Validate(); err != nil {
return nil, err
}
httpClient := &http.Client{
Timeout: 15 * time.Second,
}
server := &DctWServer{
DeBug: DeBug,
Config: config,
HttpClient: httpClient,
}
for _, f := range o {
f(server)
}
return server, nil
}
func (c *DctWServer) Sign(body, path, timestamp string) string {
str := c.Config.AppId + body + path + timestamp + c.Config.AppKey
return utils.Md5([]byte(str))
}
func (c *DctWServer) BuildAuth(signStr, timestamp string) string {
return fmt.Sprintf("%s appid=%s,timestamp=%s,sign=%s", SignType, c.Config.AppId, timestamp, signStr)
}
func (c *DctWServer) Headers(signStr, timestamp string) http.Header {
h := make(http.Header)
h.Add("Content-type", ApplicationJSON)
h.Add(SignKeyName, c.BuildAuth(signStr, timestamp))
return h
}
func (c *DctWServer) verify(authorization, path string, body []byte) error {
trimmedAuth := strings.TrimPrefix(authorization, "MD5 ")
authData := make(map[string]string)
parts := strings.Split(trimmedAuth, ",")
for _, part := range parts {
keyValue := strings.Split(part, "=")
if len(keyValue) != 2 {
return fmt.Errorf("授权信息格式错误: %s", part)
}
authData[keyValue[0]] = keyValue[1]
}
sign, ok := authData["sign"]
if !ok {
return fmt.Errorf("签名数据获取失败")
}
timestamp, ok := authData["timestamp"]
if !ok {
return fmt.Errorf("时间戳获取失败")
}
if sign == c.Sign(string(body), path, timestamp) {
return nil
}
return fmt.Errorf("验签失败")
}
func (c *DctWServer) VerifyDctW(authorization string, body []byte) error {
trimmedAuth := strings.TrimPrefix(authorization, "MD5 ")
authData := make(map[string]string)
parts := strings.Split(trimmedAuth, ",")
for _, part := range parts {
keyValue := strings.Split(part, "=")
if len(keyValue) != 2 {
return fmt.Errorf("授权信息格式错误: %s", part)
}
authData[keyValue[0]] = keyValue[1]
}
sign, ok := authData["sign"]
if !ok {
return fmt.Errorf("签名数据获取失败")
}
signStr := fmt.Sprintf("%s%s%s", c.Config.AppId, string(body), c.Config.AppKey)
if sign == utils.Md5([]byte(signStr)) {
return nil
}
return fmt.Errorf("验签失败")
}
func (c *DctWServer) Request(_ context.Context, request DctWRequest, path string) ([]byte, error) {
timestamp := fmt.Sprintf("%d", time.Now().Unix())
body, err := request.Json()
if err != nil {
return nil, err
}
signStr := c.Sign(string(body), path, timestamp)
url := fmt.Sprintf("%s%s", c.Config.BaseUri, path)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
if err != nil {
return nil, err
}
req.Header = c.Headers(signStr, timestamp)
if c.DeBug {
fmt.Printf("request.url:%s\n", url)
fmt.Printf("request.body:%s\n", string(body))
fmt.Printf("request.signStr:%s\n", signStr)
fmt.Printf("request.headers:%+v\n", req.Header)
}
resp, err := c.HttpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if c.DeBug {
fmt.Printf("resp.status:%s\n", resp.Status)
fmt.Printf("resp.body:%s\n", string(bodyBytes))
fmt.Printf("resp.headers:%+v\n", resp.Header)
}
return bodyBytes, nil
}

31
dctw/v1/core/core_test.go Normal file
View File

@ -0,0 +1,31 @@
package core
import (
"testing"
)
func TestZc_DctWServer(t *testing.T) {
server, err := NewDctWServer(
&DctWConfig{
AppId: "101",
AppKey: "5k8bJV6axIukpkwz5vdte8QCw5etlC+txi+Nu69nIhOMN4VhmcNgf/THdllpt0jO",
BaseUri: "http://openapi.1688sup.cn",
},
WithDebug(true),
)
if err != nil {
t.Fatal(err)
}
timestamp := "1731566046566"
body := "xx=1"
path := "/xx/xx"
str := server.Sign(body, path, timestamp)
authorization := server.BuildAuth(str, timestamp)
if err = server.verify(authorization, path, []byte(body)); err != nil {
t.Fatal(err)
}
t.Log("验签成功")
}

4
go.mod
View File

@ -7,11 +7,13 @@ require (
github.com/go-playground/validator/v10 v10.22.0 github.com/go-playground/validator/v10 v10.22.0
github.com/hashicorp/go-plugin v1.6.1 github.com/hashicorp/go-plugin v1.6.1
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.8.4
google.golang.org/grpc v1.64.0 google.golang.org/grpc v1.64.0
google.golang.org/protobuf v1.34.2 google.golang.org/protobuf v1.34.2
) )
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect github.com/fatih/color v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
@ -24,9 +26,11 @@ require (
github.com/mattn/go-isatty v0.0.10 // indirect github.com/mattn/go-isatty v0.0.10 // indirect
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
github.com/oklog/run v1.0.0 // indirect github.com/oklog/run v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.25.0 // indirect golang.org/x/crypto v0.25.0 // indirect
golang.org/x/net v0.27.0 // indirect golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.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/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

2
go.sum
View File

@ -62,5 +62,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/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 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,56 +0,0 @@
package manage
var m *pluginManage
func init() {
m = &pluginManage{
plugins: make(map[string]*PluginInfo),
}
}
func Load(p []*Config) error {
for _, c := range p {
err := Add(c)
if err != nil {
return err
}
}
return nil
}
func Add(c *Config) error {
err := c.Validate()
if err != nil {
return err
}
return m.add(c)
}
func Update(c *Config) error {
err := c.Validate()
if err != nil {
return err
}
err = Remove(c.Tag)
if err != nil {
return err
}
return m.add(c)
}
func Get(tag string) (*PluginInfo, error) {
p, err := m.get(tag)
if err != nil {
return nil, err
}
return p, nil
}
func Remove(tag string) error {
return m.remove(tag)
}
func Close() {
m.close()
m = nil
}

View File

@ -1,64 +1,60 @@
package manage package manage
import ( var m *pluginManage
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/shared"
)
type PluginInfo struct { func init() {
Tag string m = &pluginManage{
Impl shared.PluginService plugins: make(map[string]*PluginInfo),
cleanup func() }
} }
type pluginManage struct { func Load(p []*Config) error {
plugins map[string]*PluginInfo for _, c := range p {
} err := Add(c)
if err != nil {
func (m *pluginManage) exists(tag string) bool { return err
if _, ok := m.plugins[tag]; ok { }
return true
}
return false
}
func (m *pluginManage) add(c *Config) error {
if m.exists(c.Tag) {
return fmt.Errorf("插件已存在[%s]", c.Tag)
}
impl, cleanup, err := newClient(c)
if err != nil {
cleanup()
return err
}
m.plugins[c.Tag] = &PluginInfo{
Tag: c.Tag,
Impl: impl,
cleanup: cleanup,
} }
return nil return nil
} }
func (m *pluginManage) remove(tag string) error { func Add(c *Config) error {
if m.exists(tag) { err := c.Validate()
m.plugins[tag].cleanup() if err != nil {
delete(m.plugins, tag) return err
return nil
} }
return fmt.Errorf("插件不存在[%s]", tag) return m.add(c)
} }
func (m *pluginManage) get(tag string) (*PluginInfo, error) { func Update(c *Config) error {
if m.exists(tag) { err := c.Validate()
return m.plugins[tag], nil if err != nil {
return err
} }
return nil, fmt.Errorf("插件不存在[%s]", tag) err = Remove(c.Tag)
if err != nil {
return err
}
return m.add(c)
} }
func (m *pluginManage) close() { func Get(tag string) (*PluginInfo, error) {
for _, info := range m.plugins { p, err := m.get(tag)
if info.cleanup != nil { if err != nil {
info.cleanup() return nil, err
}
} }
return p, nil
}
func GetAll() map[string]*PluginInfo {
return m.plugins
}
func Remove(tag string) error {
return m.remove(tag)
}
func Close() {
m.close()
m = nil
} }

64
manage/plugin.go Normal file
View File

@ -0,0 +1,64 @@
package manage
import (
"fmt"
"gitea.cdlsxd.cn/sdk/plugin/shared"
)
type PluginInfo struct {
Tag string
Impl shared.PluginService
cleanup func()
}
type pluginManage struct {
plugins map[string]*PluginInfo
}
func (m *pluginManage) exists(tag string) bool {
if _, ok := m.plugins[tag]; ok {
return true
}
return false
}
func (m *pluginManage) add(c *Config) error {
if m.exists(c.Tag) {
return fmt.Errorf("插件已存在[%s]", c.Tag)
}
impl, cleanup, err := newClient(c)
if err != nil {
cleanup()
return err
}
m.plugins[c.Tag] = &PluginInfo{
Tag: c.Tag,
Impl: impl,
cleanup: cleanup,
}
return nil
}
func (m *pluginManage) remove(tag string) error {
if m.exists(tag) {
m.plugins[tag].cleanup()
delete(m.plugins, tag)
return nil
}
return fmt.Errorf("插件不存在[%s]", tag)
}
func (m *pluginManage) get(tag string) (*PluginInfo, error) {
if p, ok := m.plugins[tag]; ok {
return p, nil
}
return nil, fmt.Errorf("插件不存在[%s]", tag)
}
func (m *pluginManage) close() {
for _, info := range m.plugins {
if info.cleanup != nil {
info.cleanup()
}
}
}

View File

@ -434,6 +434,7 @@ type NotifyResponse struct {
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Result *Result `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` Result *Result `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"`
Return string `protobuf:"bytes,2,opt,name=return,proto3" json:"return,omitempty"`
} }
func (x *NotifyResponse) Reset() { func (x *NotifyResponse) Reset() {
@ -475,6 +476,13 @@ func (x *NotifyResponse) GetResult() *Result {
return nil return nil
} }
func (x *NotifyResponse) GetReturn() string {
if x != nil {
return x.Return
}
return ""
}
type OrderRequest_Order struct { type OrderRequest_Order struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -764,23 +772,24 @@ var file_proto_plugin_proto_rawDesc = []byte{
0x72, 0x69, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x72, 0x69, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12,
0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f,
0x64, 0x79, 0x22, 0x37, 0x0a, 0x0e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x64, 0x79, 0x22, 0x4f, 0x0a, 0x0e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x73,
0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x32, 0xad, 0x01, 0x0a, 0x06, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72,
0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x34, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x74,
0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x72, 0x6e, 0x32, 0xad, 0x01, 0x0a, 0x06, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x34,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4f, 0x72, 0x64, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x34, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x70,
0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x73, 0x65, 0x22, 0x00, 0x12, 0x34, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x13, 0x2e,
0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65,
0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x14, 0x2e, 0x70, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x4e, 0x6f,
0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x74, 0x69, 0x66, 0x79, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74,
0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x0f, 0x5a, 0x0d, 0x70, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f,
0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x6f, 0x74, 0x6f, 0x33, 0x65, 0x22, 0x00, 0x42, 0x0f, 0x5a, 0x0d, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@ -949,6 +949,8 @@ func (m *NotifyResponse) validate(all bool) error {
} }
} }
// no validation rules for Return
if len(errors) > 0 { if len(errors) > 0 {
return NotifyResponseMultiError(errors) return NotifyResponseMultiError(errors)
} }

View File

@ -66,4 +66,5 @@ message NotifyRequest{
} }
message NotifyResponse{ message NotifyResponse{
Result result = 1; Result result = 1;
string return = 2;
} }

32
utils/aes.go Normal file
View File

@ -0,0 +1,32 @@
package utils
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"fmt"
)
func AesDecode(data string, key []byte) (string, error) {
decoded, err := base64.StdEncoding.DecodeString(data)
if err != nil {
return "", fmt.Errorf("base64 decode error: %v", err)
}
block, err := aes.NewCipher(key)
if err != nil {
return "", fmt.Errorf("aes new cipher error: %v", err)
}
if len(decoded) < aes.BlockSize {
return "", fmt.Errorf("ciphertext too short")
}
decrypted := make([]byte, len(decoded))
mode := cipher.NewCBCDecrypter(block, make([]byte, aes.BlockSize))
mode.CryptBlocks(decrypted, decoded)
padding := decrypted[len(decrypted)-1]
decrypted = decrypted[:len(decrypted)-int(padding)]
return string(decrypted), nil
}

View File

@ -1,8 +1,12 @@
package utils package utils
import ( import (
"crypto/md5"
"encoding/hex"
"fmt" "fmt"
"os" "os"
"regexp"
"strings"
) )
// Load 获取插件目录中的文件信息 // Load 获取插件目录中的文件信息
@ -21,6 +25,29 @@ func Load(dir string) ([]string, error) {
files = append(files, fmt.Sprintf("%s/%s", dir, info.Name())) files = append(files, fmt.Sprintf("%s/%s", dir, info.Name()))
} }
} }
return files, nil return files, nil
} }
func Md5(values []byte) string {
hash := md5.Sum(values)
return strings.ToUpper(hex.EncodeToString(hash[:]))
}
// IsPhoneNumber 检查给定的字符串是否为有效的手机号码
func IsPhoneNumber(phoneNumber string) bool {
phoneRegex := `^1[34578]\d{9}$`
return regexp.MustCompile(phoneRegex).MatchString(phoneNumber)
}
// IsEmail 检查给定的字符串是否为有效的电子邮箱地址
func IsEmail(email string) bool {
var emailRegex = regexp.MustCompile(`[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
return emailRegex.MatchString(email)
}
// IsValidQQ 检查给定的字符串是否为有效的 QQ 号
func IsValidQQ(qq string) bool {
// QQ号正则表达式5到11位数字且开头不为0的情况
re := regexp.MustCompile(`^(?!0)[0-9]{5,11}$`)
return re.MatchString(qq)
}

View File

@ -5,7 +5,7 @@ import (
"testing" "testing"
) )
func Test_load(t *testing.T) { func Test_Load(t *testing.T) {
got, err := Load("../../pkg") got, err := Load("../../pkg")
if err != nil { if err != nil {
t.Errorf("load() error = %v", err) t.Errorf("load() error = %v", err)
@ -28,3 +28,14 @@ func Test_BuildPem(t *testing.T) {
puk2 := NewPublic() puk2 := NewPublic()
t.Logf("\n%s", puk2.Build("puk key")) t.Logf("\n%s", puk2.Build("puk key"))
} }
func Test_AesDecode(t *testing.T) {
key := "837c7a898810160ce5aab4c42bf22bc4"
encStr := ""
str, err := AesDecode(encStr, []byte(key))
if err != nil {
t.Errorf("AesDecode() error = %v", err)
return
}
t.Logf("encStr: %s, str: %s", encStr, str)
}