Compare commits
23 Commits
Author | SHA1 | Date |
---|---|---|
李子铭 | e0b1bf4b43 | |
李子铭 | 84a6cb208f | |
李子铭 | b71aa93e65 | |
李子铭 | 85034cfb35 | |
李子铭 | 50ab391039 | |
李子铭 | fe8e56420f | |
李子铭 | 35ccf00466 | |
李子铭 | 8102c3ffcd | |
李子铭 | eb9a1323e5 | |
李子铭 | 1b3162df5d | |
李子铭 | 990030fff3 | |
李子铭 | 0823da7786 | |
李子铭 | 3069c47115 | |
李子铭 | 5e71fabbf9 | |
李子铭 | e0985d04af | |
李子铭 | c9c91d9146 | |
李子铭 | 307205cdfd | |
李子铭 | 0ea434699a | |
李子铭 | 7cc22209f5 | |
李子铭 | aa83189b01 | |
李子铭 | 48907c4653 | |
李子铭 | a241f05370 | |
李子铭 | 83c54628dd |
|
@ -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)
|
||||||
|
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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, "充值是否成功")
|
||||||
|
}
|
|
@ -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 "未知错误,请联系平台"
|
||||||
|
}
|
|
@ -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 ""
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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 "未知错误,请联系平台"
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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, "充值是否成功")
|
||||||
|
}
|
|
@ -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 ""
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import "gitea.cdlsxd.cn/sdk/plugin/dctw/v1/core"
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
*core.DctWServer
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
4
go.mod
|
@ -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
2
go.sum
|
@ -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=
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,4 +66,5 @@ message NotifyRequest{
|
||||||
}
|
}
|
||||||
message NotifyResponse{
|
message NotifyResponse{
|
||||||
Result result = 1;
|
Result result = 1;
|
||||||
|
string return = 2;
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue