分离小米支付与taptap支付到库里面
This commit is contained in:
@@ -1,60 +1,62 @@
|
||||
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"`
|
||||
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 {
|
||||
func New(clientId string, secret string) *pTapTap {
|
||||
return &pTapTap{
|
||||
Secret: "5AFEWnadBA0NgJK2mxeBLQEde0qyIefxLSc4XKHsx9AwkQRhxzkQ9DixsOkK6gcV",
|
||||
ClientId: "mox88lbz43edfukdgk",
|
||||
OrderId: orderId,
|
||||
Secret: secret,
|
||||
ClientId: clientId,
|
||||
//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 (p *pTapTap) Sign(req *http.Request, secret string) (string, error) {
|
||||
//获取请求参数
|
||||
//req := g.RequestFromCtx(ctx).Request
|
||||
return Sign(req, secret)
|
||||
}
|
||||
|
||||
//func (p *pTapTap) SignOld(ctx context.Context, method, url string, token string, data any) (sign string, ts int64, nonce string, err error) {
|
||||
// //secret := p.Secret
|
||||
// //ts = gtime.Now().Unix()
|
||||
// //nonce = grand.S(5)
|
||||
// //header := http.Header{
|
||||
// // "Content-Type": {"Content-Type: application/json; charset=utf-8"},
|
||||
// // "X-Tap-Ts": {strconv.FormatInt(ts, 10)},
|
||||
// // "X-Tap-Nonce": {nonce},
|
||||
// //}
|
||||
// //if method == "POST" {
|
||||
// // header.Set("Content-Type", "application/json; charset=utf-8")
|
||||
// //}
|
||||
// ////ctx := context.Background()
|
||||
// //request := g.RequestFromCtx(ctx).Request
|
||||
// //body, _ := json.Marshal(data)
|
||||
// ////req, err := http.NewRequestWithContext(ctx, method, url, strings.NewReader(string(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
|
||||
|
||||
@@ -1,59 +1,129 @@
|
||||
package taptap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gclient"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"strconv"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type GetPayData struct {
|
||||
Data struct {
|
||||
Order struct {
|
||||
Order
|
||||
} `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
|
||||
type Order struct {
|
||||
OrderID string `json:"order_id"` // 订单唯一 ID
|
||||
PurchaseToken string `json:"purchase_token"` // 用于订单核销的 token
|
||||
ClientID string `json:"client_id"` // 应用的 Client ID
|
||||
OpenID string `json:"open_id"` // 用户的开放平台 ID
|
||||
UserRegion string `json:"user_region"` // 用户地区
|
||||
GoodsOpenID string `json:"goods_open_id"` // 商品唯一 ID
|
||||
GoodsName string `json:"goods_name"` // 商品名称
|
||||
Status PaymentStatus `json:"status"` // 订单状态
|
||||
Amount string `json:"amount"` // 金额(本币金额 x 1,000,000)
|
||||
Currency string `json:"currency"` // 币种
|
||||
CreateTime string `json:"create_time"` // 创建时间
|
||||
PayTime string `json:"pay_time"` // 支付时间
|
||||
Extra string `json:"extra"` // 商户自定义数据,如角色信息等,长度不超过 255 UTF-8 字符
|
||||
}
|
||||
type PaymentStatus string
|
||||
|
||||
//验证并核销订单
|
||||
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
|
||||
}
|
||||
const (
|
||||
ChargePending PaymentStatus = "charge.pending" // 待支付
|
||||
ChargeSucceeded PaymentStatus = "charge.succeeded" //支付成功
|
||||
ChargeConfirmed PaymentStatus = "charge.confirmed" //已核销
|
||||
ChargeOverdue PaymentStatus = "charge.overdue" //支付超时关闭
|
||||
RefundPending PaymentStatus = "refund.pending" //退款中
|
||||
RefundSucceeded PaymentStatus = "refund.succeeded" //退款成功
|
||||
RefundFailed PaymentStatus = "refund.failed" //退款失败
|
||||
RefundRejected PaymentStatus = "refund.rejected" //退款被拒绝
|
||||
)
|
||||
|
||||
func (p *pTapTap) get(url string, token []byte, _data ...any) (res string, err error) {
|
||||
sign, ts, nonce, err := p.Sign(url, token)
|
||||
// 查询订单信息
|
||||
func (p *pTapTap) Info(ctx context.Context, order string) (getPayData *GetPayData, err error) {
|
||||
url := fmt.Sprintf("https://cloud-payment.tapapis.cn/order/v1/info?client_id=%v&order_id=%v", p.ClientId, order)
|
||||
getPayData, err = p.get(ctx, url)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 验证并核销订单
|
||||
func (p *pTapTap) Verify(ctx context.Context, req any) (getPayData *GetPayData, err error) {
|
||||
url := fmt.Sprintf("https://cloud-payment.tapapis.cn/order/v1/verify?client_id=%v", p.ClientId)
|
||||
getPayData, err = p.get(ctx, url, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *pTapTap) get(ctx context.Context, url string, _data ...any) (getPayData *GetPayData, err error) {
|
||||
|
||||
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)
|
||||
|
||||
var header = map[string]string{
|
||||
"Content-Type": "Content-Type: application/json; charset=utf-8",
|
||||
"X-Tap-Nonce": grand.S(6),
|
||||
"X-Tap-Ts": gtime.Now().TimestampStr(),
|
||||
}
|
||||
ctx2 := context.Background()
|
||||
var method = "GET"
|
||||
if len(_data) > 0 {
|
||||
method = "POST"
|
||||
|
||||
}
|
||||
|
||||
//temp := []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"}}`)
|
||||
var body io.Reader
|
||||
if len(_data) > 0 {
|
||||
body = bytes.NewBuffer(gjson.MustEncode(_data[0]))
|
||||
} 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])
|
||||
body = bytes.NewBuffer([]byte{})
|
||||
}
|
||||
req, _ := http.NewRequestWithContext(ctx2, method, url, body)
|
||||
for k, v := range header {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
sign, err2 := p.Sign(req, p.Secret)
|
||||
if err2 != nil {
|
||||
err = err2
|
||||
return
|
||||
}
|
||||
req.Header.Set("X-Tap-Sign", sign)
|
||||
header["X-Tap-Sign"] = sign
|
||||
if len(_data) == 0 {
|
||||
_get, err = g.Client().Header(header).ContentJson().Get(gctx.New(), url)
|
||||
} else {
|
||||
_get, err = g.Client().Header(header).ContentJson().Post(gctx.New(), url, _data[0])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = _get.ReadAllString()
|
||||
getPayData = &GetPayData{}
|
||||
resData := _get.ReadAll()
|
||||
g.Dump(resData)
|
||||
if err = json.Unmarshal(resData, &getPayData); err != nil {
|
||||
return
|
||||
}
|
||||
if !getPayData.Success {
|
||||
err = errors.New(string(resData))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package taptap
|
||||
|
||||
import "github.com/gogf/gf/v2/encoding/gjson"
|
||||
|
||||
type WebhookData struct {
|
||||
Order *Order `json:"order"`
|
||||
EventType string `json:"event_type"`
|
||||
}
|
||||
|
||||
// Order 订单信息结构体
|
||||
type Order struct {
|
||||
OrderID string `json:"order_id" dc:"订单唯一ID"`
|
||||
PurchaseToken string `json:"purchase_token" dc:"用于订单核销的token"`
|
||||
ClientID string `json:"client_id" dc:"应用的Client ID"`
|
||||
OpenID string `json:"open_id" dc:"用户的开放平台ID"`
|
||||
UserRegion string `json:"user_region" dc:"用户地区"`
|
||||
GoodsOpenID string `json:"goods_open_id" dc:"商品唯一ID"`
|
||||
GoodsName string `json:"goods_name" dc:"商品名称"`
|
||||
Status string `json:"status" dc:"订单状态"`
|
||||
Amount string `json:"amount" dc:"金额(本币金额x1,000,000)"`
|
||||
Currency string `json:"currency" dc:"币种"`
|
||||
CreateTime string `json:"create_time" dc:"创建时间"`
|
||||
PayTime string `json:"pay_time" dc:"支付时间"`
|
||||
Extra string `json:"extra" dc:"商户自定义数据,如角色信息等,长度不超过255 UTF-8字符"`
|
||||
}
|
||||
|
||||
func (p *pTapTap) Webhook(body []byte) (res string, err error) {
|
||||
var data *WebhookData
|
||||
gjson.DecodeTo(body, &data)
|
||||
|
||||
switch data.EventType {
|
||||
case "charge.succeeded": //充值成功
|
||||
//todo 处理订单信息
|
||||
|
||||
case "refund.succeeded": //退款成功
|
||||
case "refund.failed": //退款失败
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user