server/pkg/payment/alipay/alipay.go

115 lines
2.6 KiB
Go

package alipay
import (
"context"
"net/url"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
"github.com/pkg/errors"
"github.com/smartwalle/alipay/v3"
)
type Config struct {
AppId string
PrivateKey string
PublicKey string
InvoiceName string
NotifyURL string
Sandbox bool
}
type Notification struct {
OrderNo string
Amount int64
Status Status
}
type Status string
const (
Success Status = "TRADE_SUCCESS"
Pending Status = "WAIT_BUYER_PAY"
Closed Status = "TRADE_CLOSED"
Finished Status = "TRADE_FINISHED"
Error Status = "TRADE_ERROR"
)
type Client struct {
Config
client *alipay.Client
}
type Order struct {
OrderNo string
Amount int64
}
func NewClient(c Config) *Client {
client, err := alipay.New(c.AppId, c.PrivateKey, c.Sandbox)
if err != nil {
logger.Error("[Alipay] NewClient failed: ", logger.Field("errors", err), logger.Field("config", c))
return nil
}
err = client.LoadAliPayPublicKey(c.PublicKey)
if err != nil {
logger.Error("[Alipay] NewClient failed: ", logger.Field("errors", err), logger.Field("config", c))
}
return &Client{
Config: c,
client: client,
}
}
func (c *Client) PreCreateTrade(ctx context.Context, order Order) (string, error) {
amountString := tool.FormatFloat(float64(order.Amount)/float64(100), 2)
trade, err := c.client.TradePreCreate(ctx, alipay.TradePreCreate{
Trade: alipay.Trade{
OutTradeNo: order.OrderNo,
TotalAmount: amountString,
Subject: c.InvoiceName,
NotifyURL: c.NotifyURL,
},
})
if err != nil {
return "", err
}
if trade.Code != alipay.CodeSuccess {
return "", errors.New("PreCreateTrade failed: " + trade.Msg)
}
return trade.QRCode, nil
}
func (c *Client) QueryTrade(ctx context.Context, orderNo string) (Status, error) {
trade, err := c.client.TradeQuery(ctx, alipay.TradeQuery{
OutTradeNo: orderNo,
})
if err != nil {
return Error, err
}
switch trade.TradeStatus {
case alipay.TradeStatusSuccess:
return Success, nil
case alipay.TradeStatusWaitBuyerPay:
return Pending, nil
case alipay.TradeStatusClosed:
return Closed, nil
case alipay.TradeStatusFinished:
return Finished, nil
default:
return Error, errors.New("QueryTrade failed: " + trade.Msg)
}
}
func (c *Client) DecodeNotification(form url.Values) (*Notification, error) {
notify, err := c.client.DecodeNotification(form)
if err != nil {
return nil, err
}
return &Notification{
OrderNo: notify.OutTradeNo,
Amount: int64(tool.FormatStringToFloat(notify.TotalAmount) * 100),
Status: Status(notify.TradeStatus),
}, nil
}