From 005cf11b2a6324f17aebf17f1e4dd86806f0c833 Mon Sep 17 00:00:00 2001 From: ayflying Date: Thu, 29 May 2025 11:25:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0taptap=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package/pay/taptap/sign.go | 101 +++++++++++++++++++++++++++++++++++ package/pay/taptap/taptap.go | 59 ++++++++++++++++++++ pkg/aycache/drive/file.go | 5 +- 3 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 package/pay/taptap/sign.go create mode 100644 package/pay/taptap/taptap.go diff --git a/package/pay/taptap/sign.go b/package/pay/taptap/sign.go new file mode 100644 index 0000000..4a14c87 --- /dev/null +++ b/package/pay/taptap/sign.go @@ -0,0 +1,101 @@ +package taptap + +import ( + "bytes" + "context" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "fmt" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/grand" + "io" + "net/http" + "sort" + "strconv" + "strings" +) + +type pTapTap struct { + Secret string `json:"secret" dc:"秘钥"` + OrderId string `json:"order_id" dc:"订单唯一 ID"` + ClientId string `json:"client_id" dc:"应用的 Client ID"` +} + +func New(orderId string) *pTapTap { + return &pTapTap{ + Secret: "5AFEWnadBA0NgJK2mxeBLQEde0qyIefxLSc4XKHsx9AwkQRhxzkQ9DixsOkK6gcV", + ClientId: "mox88lbz43edfukdgk", + OrderId: orderId, + } +} + +func (p *pTapTap) Sign(url string, body []byte) (sign string, ts int64, nonce string, err error) { + //nolint:gosec + secret := p.Secret + //body := gjson.MustEncode(g.Map{}) + //body := []byte(`{"event_type":"charge.succeeded","order":{"order_id":"1790288650833465345","purchase_token":"rT2Et9p0cfzq4fwjrTsGSacq0jQExFDqf5gTy1alp+Y=","client_id":"o6nD4iNavjQj75zPQk","open_id":"4+Axcl2RFgXbt6MZwdh++w==","user_region":"US","goods_open_id":"com.goods.open_id","goods_name":"TestGoodsName","status":"charge.succeeded","amount":"19000000000","currency":"USD","create_time":"1716168000","pay_time":"1716168000","extra":"1111111111111111111"}}`) + //url := "https://example.com/my-service/v1/my-method" + ts = gtime.Now().Unix() + nonce = grand.S(5) + method := "POST" + header := http.Header{ + "Content-Type": {"Content-Type: application/json; charset=utf-8"}, + "X-Tap-Ts": {strconv.FormatInt(ts, 10)}, + "X-Tap-Nonce": {nonce}, + } + ctx := context.Background() + req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewBuffer(body)) + req.Header = header + sign, err = Sign(req, secret) + if err != nil { + panic(err) + } + req.Header.Set("X-Tap-Sign", sign) + return +} + +// Sign signs the request. +func Sign(req *http.Request, secret string) (string, error) { + methodPart := req.Method + urlPathAndQueryPart := req.URL.RequestURI() + headersPart, err := getHeadersPart(req.Header) + if err != nil { + return "", err + } + bodyPart, err := io.ReadAll(req.Body) + if err != nil { + return "", err + } + signParts := methodPart + "\n" + urlPathAndQueryPart + "\n" + headersPart + "\n" + string(bodyPart) + "\n" + fmt.Println(signParts) + h := hmac.New(sha256.New, []byte(secret)) + h.Write([]byte(signParts)) + rawSign := h.Sum(nil) + sign := base64.StdEncoding.EncodeToString(rawSign) + return sign, nil +} + +// getHeadersPart returns the headers part of the request. +func getHeadersPart(header http.Header) (string, error) { + var headerKeys []string + for k, v := range header { + k = strings.ToLower(k) + if !strings.HasPrefix(k, "x-tap-") { + continue + } + if k == "x-tap-sign" { + continue + } + if len(v) > 1 { + return "", fmt.Errorf("invalid header, %q has multiple values", k) + } + headerKeys = append(headerKeys, k) + } + sort.Strings(headerKeys) + headers := make([]string, 0, len(headerKeys)) + for _, k := range headerKeys { + headers = append(headers, fmt.Sprintf("%s:%s", k, header.Get(k))) + } + return strings.Join(headers, "\n"), nil +} diff --git a/package/pay/taptap/taptap.go b/package/pay/taptap/taptap.go new file mode 100644 index 0000000..6b9db46 --- /dev/null +++ b/package/pay/taptap/taptap.go @@ -0,0 +1,59 @@ +package taptap + +import ( + "fmt" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/gclient" + "github.com/gogf/gf/v2/os/gctx" + "strconv" +) + +type GetPayData struct { + Data struct { + Order struct { + } `json:"order"` + } `json:"data"` + Success bool `json:"success"` +} + +//查询订单信息 +func (p *pTapTap) Info(orderId string, clientId string, token []byte) (res string, err error) { + url := fmt.Sprintf("https://cloud-payment.tapapis.cn/order/v1/info?client_id=%v&order_id=%v", orderId, clientId) + res, err = p.get(url, token) + return +} + +//验证并核销订单 +func (p *pTapTap) Verify(orderId string, clientId string, token []byte) (res string, err error) { + url := fmt.Sprintf("https://cloud-payment.tapapis.cn/order/v1/verify?client_id=%v", clientId) + res, err = p.get(url, token) + return +} + +func (p *pTapTap) get(url string, token []byte, _data ...any) (res string, err error) { + sign, ts, nonce, err := p.Sign(url, token) + if err != nil { + return + } + var _get *gclient.Response + if len(_data) == 0 { + _get, err = g.Client().Header(map[string]string{ + "X-Tap-Sign": sign, + "X-Tap-Nonce": nonce, + "X-Tap-Ts": strconv.FormatInt(ts, 10), + }).Get(gctx.New(), url) + + } else { + _get, err = g.Client().Header(map[string]string{ + "X-Tap-Sign": sign, + "X-Tap-Nonce": nonce, + "X-Tap-Ts": strconv.FormatInt(ts, 10), + }).Post(gctx.New(), url, _data[0]) + } + + if err != nil { + return + } + res = _get.ReadAllString() + return +} diff --git a/pkg/aycache/drive/file.go b/pkg/aycache/drive/file.go index 4f62d88..0b904c9 100644 --- a/pkg/aycache/drive/file.go +++ b/pkg/aycache/drive/file.go @@ -185,11 +185,12 @@ func (a *AdapterFile) Data(ctx context.Context) (data map[interface{}]interface{ } func (a *AdapterFile) Keys(ctx context.Context) (keys []interface{}, err error) { + keys = make([]interface{}, 0) gfile.ReadLines(fileIndex, func(text string) (err error) { - + arr := strings.Split(text, "|") + keys = append(keys, arr[0]) return }) - return }