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.ClientMiniProgram: return c.miniProgramPrepay(ctx, req) default: return nil, pkgpay.ErrUnsupportedClient } } 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 }