package radio import ( "context" "net/http" "sundynix-go/global" "sundynix-go/model/radio" "sundynix-go/model/system" "sundynix-go/utils/wechat" "github.com/gin-gonic/gin" "github.com/wechatpay-apiv3/wechatpay-go/core" "github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers" "github.com/wechatpay-apiv3/wechatpay-go/core/notify" "github.com/wechatpay-apiv3/wechatpay-go/services/payments" "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" "github.com/wechatpay-apiv3/wechatpay-go/utils" "go.uber.org/zap" "gorm.io/gorm" ) type PayService struct{} var PayServiceApp = new(PayService) // PrePay 预支付 func (s *PayService) PrePay(orderId, userId string) (resp *jsapi.PrepayWithRequestPaymentResponse, err error) { //1.查询订单和 用户 var order radio.Order err = global.DB.Where("id = ?", orderId).First(&order).Error if err != nil { return nil, err } var user system.User err = global.DB.Where("id = ?", userId).First(&user).Error if err != nil { return nil, err } payClient, err := wechat.GetWxPayClient() if err != nil { return nil, err } svc := jsapi.JsapiApiService{Client: payClient} result, _, err := svc.PrepayWithRequestPayment(context.Background(), jsapi.PrepayRequest{ Appid: core.String(global.Config.MiniProgram.AppId), Mchid: core.String(global.Config.WechatPay.MchId), Description: core.String(order.Name), OutTradeNo: core.String(order.OutTradeNo), //TimeExpire: core.Time(time.Now()), //选填 //Attach: core.String("自定义数据说明"), //选填 NotifyUrl: core.String(global.Config.WechatPay.NotifyUrl), //GoodsTag: core.String("WXG"), //选填 //SupportFapiao: core.Bool(false), //选填 Amount: &jsapi.Amount{ Currency: core.String("CNY"), Total: core.Int64(int64(order.Amount)), }, Payer: &jsapi.Payer{ Openid: core.String(user.OpenId), }, //Detail: &jsapi.Detail{ // CostPrice: core.Int64(608800), // GoodsDetail: []jsapi.GoodsDetail{jsapi.GoodsDetail{ // GoodsName: core.String("iPhoneX 256G"), // MerchantGoodsId: core.String("ABC"), // Quantity: core.Int64(1), // UnitPrice: core.Int64(828800), // WechatpayGoodsId: core.String("1001"), // }}, // InvoiceId: core.String("wx123"), //}, //选填 //SceneInfo: &jsapi.SceneInfo{ // DeviceId: core.String("013467007045764"), // PayerClientIp: core.String("14.23.150.211"), // StoreInfo: &jsapi.StoreInfo{ // Address: core.String("广东省深圳市南山区科技中一道10000号"), // AreaCode: core.String("440305"), // Id: core.String("0001"), // Name: core.String("腾讯大厦分店"), // }, //}, //SettleInfo: &jsapi.SettleInfo{ // ProfitSharing: core.Bool(false), //}, //选填 }) if err != nil { return nil, err } return result, nil } // PayCallback 支付回调 func (s *PayService) PayCallback(c *gin.Context) error { //1.加载共钥 mchPublicKeyPath := global.Config.WechatPay.PublicKeyPath mchPublicKey, err := utils.LoadPublicKeyWithPath(mchPublicKeyPath) if err != nil { return err } ctx := context.Background() //2.创建客户端 handler, err := notify.NewRSANotifyHandler(global.Config.WechatPay.MchAPIv3Key, verifiers.NewSHA256WithRSAPubkeyVerifier(global.Config.WechatPay.PublicKeyId, *mchPublicKey)) if err != nil { return err } //3.验签 解密 //将支付回调通知中的内容,解析为 payments.Transaction。 transaction := new(payments.Transaction) notifyReq, err := handler.ParseNotifyRequest(ctx, c.Request, transaction) // 4.如果验签未通过,或者解密失败 if err != nil { //应答微信 c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ "code": "FAIL", "message": "失败", }) global.Logger.Error("wxPay回调-验签或解密:", zap.Error(err)) return err } //5.应答微信 c.Status(http.StatusOK) // 6.处理通知内容 global.Logger.Info("wxPay回调-成功:", zap.Any("notifyReq", notifyReq.Summary)) //7. 异步处理数据 go func() { err = global.DB.Transaction(func(tx *gorm.DB) error { //7.1 回调记录 payNotify := radio.PayNotify{ Amount: *transaction.Amount.Total, Currency: *transaction.Amount.Currency, PayerCurrency: *transaction.Amount.PayerCurrency, PayerTotal: *transaction.Amount.PayerTotal, Appid: *transaction.Appid, MchId: *transaction.Mchid, OutTradeNo: *transaction.OutTradeNo, Attach: *transaction.Attach, BankType: *transaction.BankType, Payer: *transaction.Payer.Openid, SuccessTime: *transaction.SuccessTime, TradeState: *transaction.TradeState, TradeStateDesc: *transaction.TradeStateDesc, TradeType: *transaction.TradeType, TransactionId: *transaction.TransactionId, } if err := tx.Create(&payNotify).Error; err != nil { global.Logger.Error("wxPay回调-存储数据异常:", zap.Error(err)) return err } if payNotify.TradeState == "SUCCESS" { return OrderServiceApp.ExecuteOrderUnlock(tx, *transaction.OutTradeNo) } return nil }) }() return nil } // QueryPay 根据商户订单号查询订单状态 func (s *PayService) QueryPay(no string, userId string) (bool, error) { var order radio.Order if err := global.DB.Where("out_trade_no = ?", no).First(&order).Error; err != nil { return false, err } // 1. 如果本地已经成功,直接返回成功 if order.Status == 1 { return true, nil } //2.本地还是待支付,主动调用微信 API 查询 payClient, err := wechat.GetWxPayClient() if err != nil { return false, err } svc := jsapi.JsapiApiService{Client: payClient} resp, _, err := svc.QueryOrderByOutTradeNo(context.Background(), jsapi.QueryOrderByOutTradeNoRequest{ OutTradeNo: core.String(no), Mchid: core.String(global.Config.WechatPay.MchId), }) if err != nil { return false, err } global.Logger.Info("查询订单状态:", zap.Any("resp", resp)) state := *resp.TradeState outTradeNo := *resp.OutTradeNo if state == "SUCCESS" { // 3. 微信那边付过了,本地还没解锁,立即执行事务 err = global.DB.Transaction(func(tx *gorm.DB) error { return OrderServiceApp.ExecuteOrderUnlock(tx, outTradeNo) }) if err != nil { return false, err } } return true, nil }