feat: add payment (#132) (#250)

This commit is contained in:
Buer
2024-06-05 13:30:12 +08:00
committed by GitHub
parent e4665a16c8
commit 03d847fe68
42 changed files with 2890 additions and 20 deletions

View File

@@ -0,0 +1,80 @@
package epay
import (
"crypto/md5"
"encoding/hex"
"sort"
"strings"
"github.com/mitchellh/mapstructure"
)
type Client struct {
PayDomain string `json:"pay_domain"`
PartnerID string `json:"partner_id"`
Key string `json:"key"`
}
// FormPay 表单支付
func (c *Client) FormPay(args *PayArgs) (string, map[string]string, error) {
formPayArgs := map[string]string{
"type": string(args.Type),
"pid": c.PartnerID,
"out_trade_no": args.OutTradeNo,
"notify_url": args.NotifyUrl,
"return_url": args.ReturnUrl,
"name": args.Name,
"money": args.Money,
// "device": string(args.Device),
}
formPayArgs["sign"] = c.Sign(formPayArgs)
formPayArgs["sign_type"] = FormArgsSignType
domain := strings.TrimSuffix(c.PayDomain, "/")
return domain + FormSubmitUrl, formPayArgs, nil
}
func (c *Client) Verify(params map[string]string) (*PaymentResult, bool) {
sign := params["sign"]
tradeStatus := params["trade_status"]
if sign == "" || tradeStatus != TradeStatusSuccess {
return nil, false
}
if sign != c.Sign(params) {
return nil, false
}
var paymentResult PaymentResult
mapstructure.Decode(params, &paymentResult)
return &paymentResult, true
}
// Sign 签名
func (c *Client) Sign(args map[string]string) string {
keys := make([]string, 0, len(args))
for k := range args {
if k != "sign" && k != "sign_type" && args[k] != "" {
keys = append(keys, k)
}
}
sort.Strings(keys)
signStrs := make([]string, len(keys))
for i, k := range keys {
signStrs[i] = k + "=" + args[k]
}
signStr := strings.Join(signStrs, "&") + c.Key
h := md5.New()
h.Write([]byte(signStr))
return hex.EncodeToString(h.Sum(nil))
}

View File

@@ -0,0 +1,91 @@
package epay
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"one-api/payment/types"
"strconv"
"github.com/gin-gonic/gin"
)
type Epay struct{}
type EpayConfig struct {
PayType PayType `json:"pay_type"`
Client
}
func (e *Epay) Name() string {
return "易支付"
}
func (e *Epay) Pay(config *types.PayConfig, gatewayConfig string) (*types.PayRequest, error) {
epayConfig, err := getEpayConfig(gatewayConfig)
if err != nil {
return nil, err
}
payArgs := &PayArgs{
Type: epayConfig.PayType,
OutTradeNo: config.TradeNo,
NotifyUrl: config.NotifyURL,
ReturnUrl: config.ReturnURL,
Name: config.TradeNo,
Money: strconv.FormatFloat(config.Money, 'f', 2, 64),
}
formPayURL, formPayArgs, err := epayConfig.FormPay(payArgs)
if err != nil {
return nil, err
}
payRequest := &types.PayRequest{
Type: 1,
Data: types.PayRequestData{
URL: formPayURL,
Params: &formPayArgs,
Method: http.MethodPost,
},
}
return payRequest, nil
}
func (e *Epay) HandleCallback(c *gin.Context, gatewayConfig string) (*types.PayNotify, error) {
queryMap := make(map[string]string)
if err := c.ShouldBindQuery(&queryMap); err != nil {
c.Writer.Write([]byte("fail"))
return nil, err
}
epayConfig, err := getEpayConfig(gatewayConfig)
if err != nil {
c.Writer.Write([]byte("fail"))
return nil, fmt.Errorf("tradeNo: %s, PaymentNo: %s, err: %v", queryMap["out_trade_no"], queryMap["trade_no"], err)
}
paymentResult, success := epayConfig.Verify(queryMap)
if paymentResult != nil && success {
c.Writer.Write([]byte("success"))
payNotify := &types.PayNotify{
TradeNo: paymentResult.OutTradeNo,
GatewayNo: paymentResult.TradeNo,
}
return payNotify, nil
}
c.Writer.Write([]byte("fail"))
return nil, fmt.Errorf("tradeNo: %s, PaymentNo: %s, Verify Sign failed", queryMap["out_trade_no"], queryMap["trade_no"])
}
func getEpayConfig(gatewayConfig string) (*EpayConfig, error) {
var epayConfig EpayConfig
if err := json.Unmarshal([]byte(gatewayConfig), &epayConfig); err != nil {
return nil, errors.New("config error")
}
return &epayConfig, nil
}

View File

@@ -0,0 +1,45 @@
package epay
type PayType string
var (
EpayPay PayType = "" // 网关
Alipay PayType = "alipay" // 支付宝
Wechat PayType = "wxpay" // 微信
QQ PayType = "qqpay" // QQ
Bank PayType = "bank" // 银行
JD PayType = "jdpay" // 京东
PayPal PayType = "paypal" // PayPal
USDT PayType = "usdt" // USDT
)
// type DeviceType string
// var (
// PC DeviceType = "pc" // PC
// Mobile DeviceType = "mobile" // 移动端
// )
const (
FormArgsSignType = "MD5"
FormSubmitUrl = "/submit.php"
TradeStatusSuccess = "TRADE_SUCCESS"
)
type PayArgs struct {
Type PayType `json:"type,omitempty"`
OutTradeNo string `json:"out_trade_no"`
NotifyUrl string `json:"notify_url"`
ReturnUrl string `json:"return_url"`
Name string `json:"name"`
Money string `json:"money"`
}
type PaymentResult struct {
Type PayType `mapstructure:"type"`
TradeNo string `mapstructure:"trade_no"`
OutTradeNo string `mapstructure:"out_trade_no"`
Name string `mapstructure:"name"`
Money string `mapstructure:"money"`
TradeStatus string `mapstructure:"trade_status"`
}