直连天下,sdk开发
This commit is contained in:
parent
307205cdfd
commit
c9c91d9146
|
@ -0,0 +1,62 @@
|
|||
package card
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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
|
||||
}
|
||||
fmt.Println("result:", string(result))
|
||||
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, error) {
|
||||
result, err := c.Request(ctx, request, queryPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println("result:", string(result))
|
||||
var response *QueryResp
|
||||
if err = json.Unmarshal(result, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Card) 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,89 @@
|
|||
package card
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"gitea.cdlsxd.cn/sdk/plugin/dctw/v1/core"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCardOrder(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 := &Order{
|
||||
Number: 1,
|
||||
MerchantId: "101",
|
||||
OutTradeNo: "test_1_zltx_4",
|
||||
ProductId: "106",
|
||||
Mobile: "18666666666",
|
||||
NotifyUrl: "http://test.openapi.1688sup.cn",
|
||||
Version: "1.0",
|
||||
}
|
||||
resp, err := a.Order(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log(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: "101",
|
||||
OutTradeNo: "test_1_zltx_4",
|
||||
Version: "1.0",
|
||||
}
|
||||
resp, err := a.Query(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log(resp)
|
||||
}
|
||||
|
||||
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":"202409111714224026320002","rechargeAccount":"18512869479","sign":"474ACB521DEE99551153B6CE108FD06D","status":"01"}`)
|
||||
|
||||
headers := make(http.Header)
|
||||
headers.Set(core.SignKeyName, "MD5 appid=101,sign=292e21f3683219369cf68dede0d45730")
|
||||
headers.Set("Content-Type", core.ApplicationJSON)
|
||||
|
||||
var httpRequest = &http.Request{
|
||||
Header: http.Header{},
|
||||
Body: ioutil.NopCloser(bytes.NewBuffer(body)),
|
||||
}
|
||||
resp, err := a.Notify(context.Background(), httpRequest)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log(resp)
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package card
|
||||
|
||||
type Code string
|
||||
|
||||
const CodeSuccess Code = "0000"
|
||||
const RechargeCodeSuccess Code = "2000"
|
||||
|
||||
var ResMessageMap = map[Code]string{
|
||||
"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) IsRechargeSuccess() bool {
|
||||
return c == RechargeCodeSuccess
|
||||
}
|
||||
|
||||
func (c Code) GetMessage() string {
|
||||
if message, ok := ResMessageMap[c]; ok {
|
||||
return message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c Code) IsSuccess() bool {
|
||||
return c == CodeSuccess
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
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,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package card
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gitea.cdlsxd.cn/sdk/plugin/alipay/vo"
|
||||
"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"`
|
||||
Mobile string `json:"mobile"`
|
||||
NotifyUrl string `validate:"required" json:"notifyUrl"`
|
||||
Version string `validate:"required" json:"version"`
|
||||
}
|
||||
|
||||
type OrderResp struct {
|
||||
Code vo.Code `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
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 vo.Code `json:"code"`
|
||||
Status OrderStatus `json:"status"`
|
||||
Message string `json:"message"`
|
||||
OutTradeNo string `json:"outTradeNo"`
|
||||
CardCode string `json:"cardCode"`
|
||||
}
|
||||
|
||||
type Notify struct {
|
||||
MerchantId int `json:"merchantId"`
|
||||
OutTradeNo string `json:"outTradeNo"`
|
||||
RechargeAccount string `json:"rechargeAccount"`
|
||||
Status OrderStatus `json:"status"`
|
||||
CardCode string `json:"cardCode"`
|
||||
}
|
||||
|
||||
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
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package direct
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type Code json.Number
|
||||
|
||||
const (
|
||||
CodeSuccess Code = "2000"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (c Code) GetMessage() string {
|
||||
if message, ok := ResMessageMap[c]; ok {
|
||||
return message
|
||||
}
|
||||
return "未知错误,请联系平台"
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package direct
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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
|
||||
}
|
||||
fmt.Printf("result: %s\n", string(result))
|
||||
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,97 @@
|
|||
package direct
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"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",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a := &Direct{DctWServer: server}
|
||||
req := &Order{
|
||||
Number: 1,
|
||||
MerchantId: "23329",
|
||||
OutTradeNo: "test_1_zltx_6",
|
||||
ProductId: "106",
|
||||
AccountType: 1,
|
||||
RechargeAccount: "18666666666",
|
||||
NotifyUrl: "http://test.openapi.1688sup.cn",
|
||||
Version: "1.0",
|
||||
}
|
||||
resp, err := a.Order(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if !resp.Code.IsSuccess() {
|
||||
t.Error(resp.Message)
|
||||
return
|
||||
}
|
||||
t.Log(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: "106",
|
||||
OutTradeNo: "test_1_zltx_4",
|
||||
Version: "1.0",
|
||||
}
|
||||
resp, err := a.Query(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log(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":23329,"outTradeNo":"202409111714224026320002","rechargeAccount":"18512869479","sign":"474ACB521DEE99551153B6CE108FD06D","status":"01"}`)
|
||||
|
||||
headers := make(http.Header)
|
||||
headers.Set(core.SignKeyName, "MD5 appid=101,sign=292e21f3683219369cf68dede0d45730")
|
||||
headers.Set("Content-Type", core.ApplicationJSON)
|
||||
|
||||
var httpRequest = &http.Request{
|
||||
Header: http.Header{},
|
||||
Body: ioutil.NopCloser(bytes.NewBuffer(body)),
|
||||
}
|
||||
resp, err := a.Notify(context.Background(), httpRequest)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log(resp)
|
||||
assert.Equal(t, resp.Status.IsSuccess(), true, "充值是否成功")
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
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,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
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"` // 账号类型(1:手机号 2:QQ号 其他:0)
|
||||
RechargeAccount string `validate:"required" json:"rechargeAccount"`
|
||||
NotifyUrl string `validate:"required" json:"notifyUrl"`
|
||||
Version string `validate:"required" json:"version"`
|
||||
}
|
||||
|
||||
type OrderResp struct {
|
||||
Code Code `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 Code `json:"code"`
|
||||
Status OrderStatus `json:"status"`
|
||||
Message string `json:"message"`
|
||||
OutTradeNo string `json:"outTradeNo"`
|
||||
CardCode string `json:"cardCode"`
|
||||
}
|
||||
|
||||
type Notify struct {
|
||||
MerchantId int `json:"merchantId"`
|
||||
OutTradeNo string `json:"outTradeNo"`
|
||||
RechargeAccount string `json:"rechargeAccount"`
|
||||
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
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package direct
|
||||
|
||||
import (
|
||||
"gitea.cdlsxd.cn/sdk/plugin/utils"
|
||||
)
|
||||
|
||||
type AccountType int
|
||||
|
||||
const (
|
||||
AccountTypeDefault AccountType = iota
|
||||
AccountTypePhone
|
||||
AccountTypeQQ
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
return AccountTypeDefault
|
||||
}
|
||||
|
||||
func (o AccountType) Value() int {
|
||||
return int(o)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package api
|
||||
|
||||
import "gitea.cdlsxd.cn/sdk/plugin/dctw/v1/core"
|
||||
|
||||
type Service struct {
|
||||
*core.DctWServer
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
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("时间戳获取失败")
|
||||
}
|
||||
calculatedSign := c.Sign(string(body), path, timestamp)
|
||||
if sign == calculatedSign {
|
||||
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)
|
||||
calculatedSign := utils.Md5([]byte(signStr))
|
||||
if sign == calculatedSign {
|
||||
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)
|
||||
httpClient := &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
}
|
||||
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("url:%s\n", url)
|
||||
fmt.Printf("body:%s\n", string(body))
|
||||
fmt.Printf("signStr:%s\n", signStr)
|
||||
fmt.Printf("Header:%+v\n", req.Header)
|
||||
}
|
||||
|
||||
resp, err := 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("bodyStr:%s\n", string(bodyBytes))
|
||||
}
|
||||
|
||||
return bodyBytes, nil
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
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",
|
||||
})
|
||||
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("验签成功")
|
||||
}
|
|
@ -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
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Load 获取插件目录中的文件信息
|
||||
|
@ -24,3 +28,27 @@ func Load(dir string) ([]string, error) {
|
|||
|
||||
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"
|
||||
)
|
||||
|
||||
func Test_load(t *testing.T) {
|
||||
func Test_Load(t *testing.T) {
|
||||
got, err := Load("../../pkg")
|
||||
if err != nil {
|
||||
t.Errorf("load() error = %v", err)
|
||||
|
@ -28,3 +28,14 @@ func Test_BuildPem(t *testing.T) {
|
|||
puk2 := NewPublic()
|
||||
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