From e08fd3db13def4dbd91a353692e7084f15ba18fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E9=93=AD?= <zi__mm@163.com>
Date: Fri, 15 Nov 2024 13:55:17 +0800
Subject: [PATCH] =?UTF-8?q?=E7=9B=B4=E8=BF=9E=E5=A4=A9=E4=B8=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 plugins/zltx_v1/go.mod                   |  36 +++++++
 plugins/zltx_v1/go.sum                   |  72 ++++++++++++++
 plugins/zltx_v1/internal/transform.go    | 117 +++++++++++++++++++++++
 plugins/zltx_v1/internal/zltx_v1.go      | 104 ++++++++++++++++++++
 plugins/zltx_v1/internal/zltx_v1_test.go |  97 +++++++++++++++++++
 plugins/zltx_v1/main.go                  |  15 +++
 6 files changed, 441 insertions(+)
 create mode 100644 plugins/zltx_v1/go.mod
 create mode 100644 plugins/zltx_v1/go.sum
 create mode 100644 plugins/zltx_v1/internal/transform.go
 create mode 100644 plugins/zltx_v1/internal/zltx_v1.go
 create mode 100644 plugins/zltx_v1/internal/zltx_v1_test.go
 create mode 100644 plugins/zltx_v1/main.go

diff --git a/plugins/zltx_v1/go.mod b/plugins/zltx_v1/go.mod
new file mode 100644
index 0000000..531a0af
--- /dev/null
+++ b/plugins/zltx_v1/go.mod
@@ -0,0 +1,36 @@
+module plugins/zltx_v1
+
+go 1.22.2
+
+require (
+	gitea.cdlsxd.cn/sdk/plugin v1.0.7
+	github.com/carlmjohnson/requests v0.24.2
+	github.com/go-playground/validator/v10 v10.22.0
+	github.com/hashicorp/go-plugin v1.6.1
+	github.com/stretchr/testify v1.9.0
+)
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/fatih/color v1.7.0 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/golang/protobuf v1.5.4 // indirect
+	github.com/hashicorp/go-hclog v0.14.1 // indirect
+	github.com/hashicorp/yamux v0.1.1 // indirect
+	github.com/leodido/go-urn v1.4.0 // indirect
+	github.com/mattn/go-colorable v0.1.4 // indirect
+	github.com/mattn/go-isatty v0.0.10 // indirect
+	github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
+	github.com/oklog/run v1.0.0 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	golang.org/x/crypto v0.25.0 // indirect
+	golang.org/x/net v0.27.0 // indirect
+	golang.org/x/sys v0.22.0 // indirect
+	golang.org/x/text v0.16.0 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
+	google.golang.org/grpc v1.64.0 // indirect
+	google.golang.org/protobuf v1.34.2 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/plugins/zltx_v1/go.sum b/plugins/zltx_v1/go.sum
new file mode 100644
index 0000000..6979461
--- /dev/null
+++ b/plugins/zltx_v1/go.sum
@@ -0,0 +1,72 @@
+gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca h1:snL161P7OynMA8hRVMLDjwnzZA2Q4mePg/iT/dyIfzA=
+gitea.cdlsxd.cn/sdk/plugin v0.0.0-20240911021858-7f3ba37bbbca/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
+gitea.cdlsxd.cn/sdk/plugin v1.0.6 h1:fcQrgdRT4zVmxqK8rsB4Oo7jnhnXDqsgQ8GagR+DSic=
+gitea.cdlsxd.cn/sdk/plugin v1.0.6/go.mod h1:cd+ZFTmd/ZxrrVc1OZCkrh2wAMPDaAa8ce13FAAkBg0=
+gitea.cdlsxd.cn/sdk/plugin v1.0.7 h1:FlgY9vznwYHXBXMe+d5DB/QXxWWlilom0DIxa2Z/QEk=
+gitea.cdlsxd.cn/sdk/plugin v1.0.7/go.mod h1:FLuWLP2QP2aBzI2HCdZ7tvl1ieMuMOpTWvItmNGCeGA=
+github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
+github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
+github.com/carlmjohnson/requests v0.24.2 h1:JDakhAmTIKL/qL/1P7Kkc2INGBJIkIFP6xUeUmPzLso=
+github.com/carlmjohnson/requests v0.24.2/go.mod h1:duYA/jDnyZ6f3xbcF5PpZ9N8clgopubP2nK5i6MVMhU=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
+github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
+github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
+github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
+github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI=
+github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
+github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
+github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
+github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
+github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
+github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
+github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
+github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
+golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/plugins/zltx_v1/internal/transform.go b/plugins/zltx_v1/internal/transform.go
new file mode 100644
index 0000000..2b168ed
--- /dev/null
+++ b/plugins/zltx_v1/internal/transform.go
@@ -0,0 +1,117 @@
+package internal
+
+import (
+	"encoding/json"
+	"fmt"
+	"gitea.cdlsxd.cn/sdk/plugin/dctw/v1/api/direct"
+	"gitea.cdlsxd.cn/sdk/plugin/dctw/v1/core"
+	"gitea.cdlsxd.cn/sdk/plugin/proto"
+	"github.com/go-playground/validator/v10"
+)
+
+type Config struct {
+	AppId      string `json:"app_id" validate:"required"`
+	AppKey     string `json:"app_key" validate:"required"`
+	BaseUri    string `json:"base_uri" validate:"required"`
+	NotifyUrl  string `json:"notify_url" validate:"required"`
+	MerchantId string `json:"merchant_id" validate:"required"`
+}
+
+func (c *Config) validate() error {
+	err := validator.New().Struct(c)
+	if err != nil {
+		for _, err = range err.(validator.ValidationErrors) {
+			return fmt.Errorf("配置参数有误:" + err.Error())
+		}
+	}
+	return nil
+}
+
+func transConfig(config []byte) (*Config, error) {
+	var c Config
+	err := json.Unmarshal(config, &c)
+	if err != nil {
+		return nil, err
+	}
+	if err = c.validate(); err != nil {
+		return nil, err
+	}
+	return &c, nil
+}
+
+func (c *Config) server() (*core.DctWServer, error) {
+	return core.NewDctWServer(
+		&core.DctWConfig{
+			AppId:   c.AppId,
+			AppKey:  c.AppKey,
+			BaseUri: c.BaseUri,
+		},
+		core.WithDebug(false),
+	)
+}
+
+func (c *Config) orderReq(in *proto.OrderRequest) *direct.Order {
+	var a direct.AccountType
+	return &direct.Order{
+		Number:          in.Order.Quantity,
+		MerchantId:      c.MerchantId,
+		OutTradeNo:      in.Order.OrderNo,
+		ProductId:       in.Product.ProductNo,
+		AccountType:     a.AccountType(in.Order.Account),
+		RechargeAccount: in.Order.Account,
+		NotifyUrl:       c.NotifyUrl,
+		Version:         "1.0",
+	}
+}
+
+func orderResp(request *proto.OrderRequest, resp *direct.OrderResp) *proto.OrderResponse {
+	data, _ := json.Marshal(resp)
+	return &proto.OrderResponse{
+		Result: &proto.Result{
+			Status:  proto.Status_ING,
+			OrderNo: request.Order.OrderNo,
+			TradeNo: resp.TradeNo,
+			Message: resp.Message,
+			Data:    data,
+		},
+	}
+}
+
+func (c *Config) queryReq(in *proto.QueryRequest) *direct.Query {
+	return &direct.Query{
+		MerchantId: c.AppId,
+		OutTradeNo: in.Order.OrderNo,
+		Version:    "1.0",
+	}
+}
+
+func queryResp(request *proto.QueryRequest, resp *direct.QueryResp) (*proto.QueryResponse, error) {
+	data, _ := json.Marshal(resp)
+	pb := &proto.QueryResponse{
+		Result: &proto.Result{
+			Status:  resp.Status.GetOrderStatus(),
+			OrderNo: request.Order.OrderNo,
+			TradeNo: request.Order.TradeNo,
+			Message: resp.Message,
+			Data:    data,
+			Extra:   nil,
+		},
+	}
+	return pb, nil
+}
+
+func notifyResp(resp *direct.Notify) (*proto.NotifyResponse, error) {
+	data, _ := json.Marshal(resp)
+	pb := &proto.NotifyResponse{
+		Result: &proto.Result{
+			Status:  resp.Status.GetOrderStatus(),
+			OrderNo: resp.OutTradeNo,
+			TradeNo: "",
+			Message: "",
+			Data:    data,
+			Extra:   nil,
+		},
+		Return: "success",
+	}
+	return pb, nil
+}
diff --git a/plugins/zltx_v1/internal/zltx_v1.go b/plugins/zltx_v1/internal/zltx_v1.go
new file mode 100644
index 0000000..5137b12
--- /dev/null
+++ b/plugins/zltx_v1/internal/zltx_v1.go
@@ -0,0 +1,104 @@
+package internal
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"gitea.cdlsxd.cn/sdk/plugin/dctw/v1/api/direct"
+	"gitea.cdlsxd.cn/sdk/plugin/proto"
+	"io/ioutil"
+	"net/http"
+	"strings"
+)
+
+// 插件通信信息,若不对应则会报错panic
+const (
+	Tag         = "zltx"
+	Version     = 1
+	CookieKey   = "zltx"
+	CookieValue = "zltx"
+)
+
+type ZLTXV1Service struct{}
+
+func (p *ZLTXV1Service) Order(ctx context.Context, request *proto.OrderRequest) (*proto.OrderResponse, error) {
+	c, err := transConfig(request.Config)
+	if err != nil {
+		return nil, err
+	}
+
+	server, err := c.server()
+	if err != nil {
+		return nil, err
+	}
+
+	a := &direct.Direct{DctWServer: server}
+	resp, err := a.Order(ctx, c.orderReq(request))
+	if err != nil {
+		return nil, err
+	}
+	if !resp.GetCode().IsSuccess() {
+		return nil, fmt.Errorf(resp.Message)
+	}
+
+	return orderResp(request, resp), nil
+}
+
+func (p *ZLTXV1Service) Query(ctx context.Context, request *proto.QueryRequest) (*proto.QueryResponse, error) {
+	c, err := transConfig(request.Config)
+	if err != nil {
+		return nil, err
+	}
+
+	server, err := c.server()
+	if err != nil {
+		return nil, err
+	}
+
+	a := &direct.Direct{DctWServer: server}
+	resp, err := a.Query(ctx, c.queryReq(request))
+	if err != nil {
+		return nil, err
+	}
+	if !resp.GetCode().IsSuccess() {
+		return nil, fmt.Errorf(resp.Message)
+	}
+
+	return queryResp(request, resp)
+}
+
+func (p *ZLTXV1Service) Notify(ctx context.Context, request *proto.NotifyRequest) (*proto.NotifyResponse, error) {
+	c, err := transConfig(request.Config)
+	if err != nil {
+		return nil, err
+	}
+
+	server, err := c.server()
+	if err != nil {
+		return nil, err
+	}
+
+	httpHeaders := make(http.Header)
+	if err = json.Unmarshal(request.Headers, &httpHeaders); err != nil {
+		return nil, fmt.Errorf("headers Unmarshal err [%v]", err)
+	}
+	newHeaders := make(http.Header)
+	for key, values := range httpHeaders {
+		newKey := strings.Title(strings.ToLower(key))
+		newHeaders[newKey] = values
+	}
+
+	req := &http.Request{
+		Header: newHeaders,
+		Body:   ioutil.NopCloser(bytes.NewBuffer(request.Body)),
+	}
+
+	a := &direct.Direct{DctWServer: server}
+	resp, err := a.Notify(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+
+	return notifyResp(resp)
+}
diff --git a/plugins/zltx_v1/internal/zltx_v1_test.go b/plugins/zltx_v1/internal/zltx_v1_test.go
new file mode 100644
index 0000000..0e5f81e
--- /dev/null
+++ b/plugins/zltx_v1/internal/zltx_v1_test.go
@@ -0,0 +1,97 @@
+package internal
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"gitea.cdlsxd.cn/sdk/plugin/proto"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+var zltx = &ZLTXV1Service{}
+
+func config() []byte {
+	c := &Config{
+		AppId:      "1",
+		AppKey:     "1e2bf7a04b8b1e6be5dc78d04e8639c9",
+		BaseUri:    "http://test.openapi.1688sup.cn",
+		NotifyUrl:  "https://gateway.dev.cdlsxd.cn/yxh5api/v1/order/direct/notify",
+		MerchantId: "25537",
+	}
+	marshal, _ := json.Marshal(c)
+	return marshal
+}
+
+func TestConfig(t *testing.T) {
+	t.Run("TestConfig", func(t *testing.T) {
+		c := config()
+		fmt.Printf("%s\n", string(c))
+		assert.NotEmpty(t, c)
+	})
+}
+
+func TestOrder(t *testing.T) {
+	request := &proto.OrderRequest{
+		Config: config(),
+		Order: &proto.OrderRequest_Order{
+			OrderNo:  "test_zltx_v1_direct_2",
+			Account:  "18666666666",
+			Quantity: 1,
+			Extra:    nil,
+		},
+		Product: &proto.OrderRequest_Product{
+			ProductNo: "101",
+			Extra:     []byte(`{}`),
+		},
+	}
+
+	t.Run("TestOrder", func(t *testing.T) {
+		got, err := zltx.Order(context.Background(), request)
+		if err != nil {
+			t.Errorf("Order() error = %v", err)
+			return
+		}
+		t.Logf("%+v\n", got)
+		assert.Equal(t, int(proto.Status_ING), int(got.Result.Status))
+	})
+}
+
+func TestQuery(t *testing.T) {
+	request := &proto.QueryRequest{
+		Config: config(),
+		Order: &proto.QueryRequest_Order{
+			OrderNo: "test_zltx_v1_direct_1",
+			TradeNo: "",
+			Account: "",
+			Extra:   nil,
+		},
+	}
+	t.Run("TestQuery", func(t *testing.T) {
+		got, err := zltx.Query(context.Background(), request)
+		if err != nil {
+			t.Errorf("Query() error = %v", err)
+			return
+		}
+		t.Logf("%+v\n", got)
+		assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
+	})
+}
+
+func TestNotify(t *testing.T) {
+	in := &proto.NotifyRequest{
+		Config:  config(),
+		Queries: nil,
+		Headers: []byte(`{"Content-Type":["application/json"],"Authorization":["MD5 appid=101,sign=292e21f3683219369cf68dede0d45730"]`),
+		Body:    []byte(`{"merchantId":23329,"outTradeNo":"202409111714224026320002","rechargeAccount":"18512869479","sign":"474ACB521DEE99551153B6CE108FD06D","status":"01"}`),
+	}
+	t.Run("TestNotify", func(t *testing.T) {
+		got, err := zltx.Notify(context.Background(), in)
+		if !assert.Nil(t, err) {
+			t.Errorf("Notify() error = %v", err)
+			return
+		}
+		fmt.Printf("%s \n", got.String())
+		assert.Equal(t, int(proto.Status_SUCCESS), int(got.Result.Status))
+	})
+}
diff --git a/plugins/zltx_v1/main.go b/plugins/zltx_v1/main.go
new file mode 100644
index 0000000..27083cb
--- /dev/null
+++ b/plugins/zltx_v1/main.go
@@ -0,0 +1,15 @@
+package main
+
+import (
+	"gitea.cdlsxd.cn/sdk/plugin/shared"
+	"github.com/hashicorp/go-plugin"
+	"plugins/zltx_v1/internal"
+)
+
+func main() {
+	plugin.Serve(&plugin.ServeConfig{
+		HandshakeConfig: shared.HandshakeConfig(internal.Version, internal.CookieKey, internal.CookieValue),
+		Plugins:         shared.PluginSet(shared.NewPlugin(&internal.ZLTXV1Service{}, internal.Tag)),
+		GRPCServer:      plugin.DefaultGRPCServer,
+	})
+}