142 lines
3.4 KiB
Go
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.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
|
|
}
|