Files
lone-services/pkg/payment/wechat/channel.go
T
2026-10-05 17:33:44 +08:00

142 lines
3.4 KiB
Go

package wechat
import (
"bytes"
"context"
"fmt"
"net/http"
pkgpay "lone-services/pkg/payment"
wxpay "lone-services/pkg/wechat/pay"
)
// Channel wraps WeChat Pay APIv3 client.
type Channel struct {
client *wxpay.Client
}
func NewChannel(cfg wxpay.Config) (*Channel, error) {
c, err := wxpay.NewClient(cfg)
if err != nil {
return nil, err
}
return &Channel{client: c}, nil
}
func (c *Channel) Name() string {
return pkgpay.ChannelWechat
}
func (c *Channel) Prepay(ctx context.Context, client string, req *pkgpay.PrepayRequest) (*pkgpay.PrepayResult, error) {
if req == nil {
return nil, fmt.Errorf("prepay request is required")
}
if client == "" {
client = pkgpay.ClientMiniProgram
}
switch client {
case pkgpay.ClientApp, pkgpay.ClientH5:
return nil, pkgpay.ErrUnsupportedClient
default:
return c.miniProgramPrepay(ctx, req)
}
}
func (c *Channel) miniProgramPrepay(ctx context.Context, req *pkgpay.PrepayRequest) (*pkgpay.PrepayResult, error) {
currency := req.Currency
if currency == "" {
currency = "CNY"
}
result, err := c.client.MiniProgramPay(ctx, &wxpay.JsapiPrepayRequest{
Description: req.Description,
OutTradeNo: req.OutTradeNo,
Attach: req.Attach,
NotifyURL: req.NotifyURL,
Amount: wxpay.Amount{
Total: req.AmountFen,
Currency: currency,
},
Payer: wxpay.Payer{OpenID: req.OpenID},
})
if err != nil {
return nil, err
}
payload := map[string]any{}
if result.Pay != nil {
payload["timeStamp"] = result.Pay.TimeStamp
payload["nonceStr"] = result.Pay.NonceStr
payload["package"] = result.Pay.Package
payload["signType"] = result.Pay.SignType
payload["paySign"] = result.Pay.PaySign
}
return &pkgpay.PrepayResult{
PrepayID: result.PrepayID,
Payload: payload,
}, nil
}
func (c *Channel) Refund(ctx context.Context, req *pkgpay.RefundRequest) (*pkgpay.RefundResult, error) {
if req == nil {
return nil, fmt.Errorf("refund request is required")
}
currency := req.Currency
if currency == "" {
currency = "CNY"
}
result, err := c.client.Refund(ctx, &wxpay.RefundRequest{
OutTradeNo: req.OutTradeNo,
TransactionID: req.TransactionID,
OutRefundNo: req.OutRefundNo,
Reason: req.Reason,
NotifyURL: req.NotifyURL,
Amount: wxpay.RefundAmount{
Refund: req.RefundFen,
Total: req.TotalFen,
Currency: currency,
},
})
if err != nil {
return nil, err
}
return &pkgpay.RefundResult{
RefundID: result.RefundID,
OutRefundNo: result.OutRefundNo,
TransactionID: result.TransactionID,
OutTradeNo: result.OutTradeNo,
Status: result.Status,
}, nil
}
func (c *Channel) ParsePayNotify(_ context.Context, headers map[string]string, body []byte) (*pkgpay.NotifyResult, error) {
httpReq, err := http.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
if err != nil {
return nil, err
}
for k, v := range headers {
httpReq.Header.Set(k, v)
}
notify, err := c.client.ParseNotify(httpReq)
if err != nil {
return nil, err
}
if notify.EventType != "TRANSACTION.SUCCESS" {
return &pkgpay.NotifyResult{Success: false, TradeState: notify.EventType}, nil
}
tx, err := c.client.DecryptNotify(notify)
if err != nil {
return nil, err
}
return &pkgpay.NotifyResult{
OutTradeNo: tx.OutTradeNo,
TransactionID: tx.TransactionID,
TradeState: tx.TradeState,
AmountFen: tx.Amount.Total,
Success: tx.TradeState == "SUCCESS",
Attach: tx.Attach,
}, nil
}