mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-11-17 13:43:42 +08:00
173
payment/gateway/alipay/payment.go
Normal file
173
payment/gateway/alipay/payment.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package alipay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/smartwalle/alipay/v3"
|
||||
"net/http"
|
||||
"net/url"
|
||||
sysconfig "one-api/common/config"
|
||||
"one-api/payment/types"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Alipay struct{}
|
||||
|
||||
type AlipayConfig struct {
|
||||
AppID string `json:"app_id"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
PublicKey string `json:"public_key"`
|
||||
PayType PayType `json:"pay_type"`
|
||||
}
|
||||
|
||||
var client *alipay.Client
|
||||
|
||||
const isProduction bool = true
|
||||
|
||||
func (a *Alipay) Name() string {
|
||||
return "支付宝当面付"
|
||||
}
|
||||
|
||||
func (a *Alipay) InitClient(config *AlipayConfig) error {
|
||||
var err error
|
||||
client, err = alipay.New(config.AppID, config.PrivateKey, isProduction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.LoadAliPayPublicKey(config.PublicKey)
|
||||
}
|
||||
|
||||
func (a *Alipay) Pay(config *types.PayConfig, gatewayConfig string) (*types.PayRequest, error) {
|
||||
alipayConfig, err := getAlipayConfig(gatewayConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if client == nil {
|
||||
err := a.InitClient(alipayConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if alipayConfig.PayType != PagePay {
|
||||
var p = alipay.TradePreCreate{}
|
||||
p.OutTradeNo = config.TradeNo
|
||||
p.TotalAmount = strconv.FormatFloat(config.Money, 'f', 2, 64)
|
||||
p.Subject = sysconfig.SystemName + "-Token充值:" + p.TotalAmount
|
||||
p.NotifyURL = config.NotifyURL
|
||||
p.ReturnURL = config.ReturnURL
|
||||
ctx := context.Background()
|
||||
alipayRes, err := client.TradePreCreate(ctx, p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("alipay trade precreate failed: %s", alipayRes.Msg)
|
||||
}
|
||||
if !alipayRes.IsSuccess() {
|
||||
return nil, fmt.Errorf("alipay trade precreate failed: %s", alipayRes.Msg)
|
||||
}
|
||||
if alipayRes.Code != "10000" {
|
||||
return nil, fmt.Errorf("alipay trade precreate failed: %s", alipayRes.Msg)
|
||||
}
|
||||
payRequest := &types.PayRequest{
|
||||
Type: 2,
|
||||
Data: types.PayRequestData{
|
||||
URL: alipayRes.QRCode,
|
||||
Method: http.MethodGet,
|
||||
},
|
||||
}
|
||||
return payRequest, nil
|
||||
} else {
|
||||
var p = alipay.TradePagePay{}
|
||||
p.OutTradeNo = config.TradeNo
|
||||
p.TotalAmount = strconv.FormatFloat(config.Money, 'f', 2, 64)
|
||||
p.Subject = sysconfig.SystemName + "-Token充值:" + p.TotalAmount
|
||||
p.NotifyURL = config.NotifyURL
|
||||
p.ReturnURL = config.ReturnURL
|
||||
alipayRes, err := client.TradePagePay(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("alipay trade precreate failed: %s", err.Error())
|
||||
}
|
||||
payUrl, parms, err := extractURLAndParams(alipayRes.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("alipay trade precreate failed: %s", err.Error())
|
||||
}
|
||||
payRequest := &types.PayRequest{
|
||||
Type: 1,
|
||||
Data: types.PayRequestData{
|
||||
URL: payUrl,
|
||||
Params: parms,
|
||||
Method: http.MethodGet,
|
||||
},
|
||||
}
|
||||
return payRequest, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (a *Alipay) HandleCallback(c *gin.Context, gatewayConfig string) (*types.PayNotify, error) {
|
||||
// 获取通知参数
|
||||
params := c.Request.URL.Query()
|
||||
if err := c.Request.ParseForm(); err != nil {
|
||||
c.Writer.Write([]byte("failure"))
|
||||
return nil, fmt.Errorf("Alipay params failed: %v", err)
|
||||
}
|
||||
for k, v := range c.Request.PostForm {
|
||||
params[k] = v
|
||||
}
|
||||
// 验证通知签名
|
||||
if err := client.VerifySign(params); err != nil {
|
||||
c.Writer.Write([]byte("failure"))
|
||||
return nil, fmt.Errorf("Alipay Signature verification failed: %v", err)
|
||||
}
|
||||
//解析通知内容
|
||||
var noti, err = client.DecodeNotification(params)
|
||||
if err != nil {
|
||||
c.Writer.Write([]byte("failure"))
|
||||
return nil, fmt.Errorf("Alipay Error decoding notification: %v", err)
|
||||
}
|
||||
|
||||
if noti.TradeStatus == alipay.TradeStatusSuccess {
|
||||
payNotify := &types.PayNotify{
|
||||
TradeNo: noti.OutTradeNo,
|
||||
GatewayNo: noti.TradeNo,
|
||||
}
|
||||
alipay.ACKNotification(c.Writer)
|
||||
return payNotify, nil
|
||||
}
|
||||
c.Writer.Write([]byte("failure"))
|
||||
return nil, fmt.Errorf("trade status not success")
|
||||
}
|
||||
|
||||
func getAlipayConfig(gatewayConfig string) (*AlipayConfig, error) {
|
||||
var alipayConfig AlipayConfig
|
||||
if err := json.Unmarshal([]byte(gatewayConfig), &alipayConfig); err != nil {
|
||||
return nil, errors.New("config error")
|
||||
}
|
||||
|
||||
return &alipayConfig, nil
|
||||
}
|
||||
|
||||
// extractURLAndParams 从给定的原始 URL 中提取网址和参数,并将参数转换为 map[string]string
|
||||
func extractURLAndParams(rawURL string) (string, map[string]string, error) {
|
||||
// 解析 URL
|
||||
parsedURL, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// 提取网址
|
||||
baseURL := fmt.Sprintf("%s://%s%s", parsedURL.Scheme, parsedURL.Host, parsedURL.Path)
|
||||
|
||||
// 提取参数并转换成 map[string]string
|
||||
params := parsedURL.Query()
|
||||
paramMap := make(map[string]string)
|
||||
for key, values := range params {
|
||||
// 由于 URL 参数可能有多个值,这里只取第一个值
|
||||
paramMap[key] = values[0]
|
||||
}
|
||||
|
||||
return baseURL, paramMap, nil
|
||||
}
|
||||
25
payment/gateway/alipay/type.go
Normal file
25
payment/gateway/alipay/type.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package alipay
|
||||
|
||||
type PayType string
|
||||
|
||||
var (
|
||||
FacePay PayType = "facepay" // 当面付
|
||||
PagePay PayType = "pagepay" // 跳转支付
|
||||
|
||||
)
|
||||
|
||||
type PayArgs struct {
|
||||
AppID string `json:"app_id"`
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
NotifyUrl string `json:"notify_url"`
|
||||
ReturnUrl string `json:"return_url"`
|
||||
Subject string `json:"subject"`
|
||||
TotalAmount string `json:"total_amount"`
|
||||
}
|
||||
|
||||
type PaymentResult struct {
|
||||
OutTradeNo string `mapstructure:"out_trade_no"`
|
||||
TradeNo string `mapstructure:"trade_no"`
|
||||
TotalAmount string `mapstructure:"total_amount"`
|
||||
TradeStatus string `mapstructure:"trade_status"`
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
"one-api/payment/gateway/alipay"
|
||||
"one-api/payment/gateway/epay"
|
||||
"one-api/payment/types"
|
||||
|
||||
@@ -17,4 +18,5 @@ var Gateways = make(map[string]PaymentProcessor)
|
||||
|
||||
func init() {
|
||||
Gateways["epay"] = &epay.Epay{}
|
||||
Gateways["alipay"] = &alipay.Alipay{}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user