package wechat import ( gfmt "fmt" "git.echol.cn/loser/lckt/global" "git.echol.cn/loser/lckt/model/app" "github.com/ArtisanCloud/PowerLibs/v3/fmt" "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models" "github.com/ArtisanCloud/PowerWeChat/v3/src/payment" wxRequst "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/notify/request" "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/order/request" "github.com/gin-gonic/gin" "go.uber.org/zap" "log" "math/rand" "time" ) var WechatPay *payment.Payment // InitWechatPay 初始化微信支付 func InitWechatPay() { PaymentService, err := payment.NewPayment(&payment.UserConfig{ AppID: "wx3d21df18d7f8f9fc", // 小程序、公众号或者企业微信的appid MchID: global.GVA_CONFIG.Pays[0].MchId, // 商户号 appID MchApiV3Key: "1a3sd8561d5179Df152D4789aD38wG9s", // 微信V3接口调用必填 Key: "57s14dFG915486Sd5617f23d45f671Ad", CertPath: `-----BEGIN CERTIFICATE----- MIIEKzCCAxOgAwIBAgIUWaiR+0A+x6HPIJDbnI7HBL1DsQEwDQYJKoZIhvcNAQEL BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg Q0EwHhcNMjUwNzIzMDU1MDU5WhcNMzAwNzIyMDU1MDU5WjCBhDETMBEGA1UEAwwK MTY0Njg3NDc1MzEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTAwLgYDVQQL DCfmtbflj6PpvpnljY7pk4HlnZrmiJDnlLXlrZDllYbliqHllYbooYwxCzAJBgNV BAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEP ADCCAQoCggEBAMUD4rf7ct0zUxJKGww+8c38L/IBx9yeNpOc7lav+CEwqZvieesy p1I2hbY+hwOZm23//0Vxcnl6fkr5wdidE/RsN808sGkYsetJ6q7LZsgsxnbaQd7w rBc4GlOh1Q4teErbjNTUMuozYzikEjMjgZMxs7i5WJnFMMVvgJS4681UKH8MQG93 BnaaD5zEyofAnOov7mvC0KZ0RpqwiLdUsMaPzYILzccOv7Tj82Z/qLw8AFcI+7L1 mq8trZxmSPXP5Fypz+1VKLKR9MaELUuQLPouewDjXkKTBnxkhoWow6huuz3qyUUo i7uJtnmDn6Eu+60ch61BB5ws3rhWDpltR/0CAwEAAaOBuTCBtjAJBgNVHRMEAjAA MAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2 Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJD MDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJC MjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQBk WmviQM60S/l4I4/RtQWtiZS+85fk2ePVSk5gBUS27W4yTw+aXy5RYaCLcOVXTXje PpAfE7QvkjZxMg/k0SJR0/NbgDNF6xuCbMj78+czWVCkqHwPjY4hWRdsM9s44Orp lwFrG/UNRhPgXcdwNQj8MNzy3nGeO0HWbJFBWcuRhSSwXpxv3Sh6pZviL3QA+lrV 7QB7d0fEu8sSGbwnIB0oXl7y78l2/D20p57TKHsq5IPfQfDdExNXKV60ikMt/MdP C4ygbeRpSAobr6qMTXcpWlbH3KfIPOUFny3nkN8C6lm//QUl7ynOXkECcmIHY6XF yMx70ORdoBl1DaZ369P9 -----END CERTIFICATE----- `, // 微信V2接口调用必填 KeyPath: `-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFA+K3+3LdM1MS ShsMPvHN/C/yAcfcnjaTnO5Wr/ghMKmb4nnrMqdSNoW2PocDmZtt//9FcXJ5en5K +cHYnRP0bDfNPLBpGLHrSequy2bILMZ22kHe8KwXOBpTodUOLXhK24zU1DLqM2M4 pBIzI4GTMbO4uViZxTDFb4CUuOvNVCh/DEBvdwZ2mg+cxMqHwJzqL+5rwtCmdEaa sIi3VLDGj82CC83HDr+04/Nmf6i8PABXCPuy9ZqvLa2cZkj1z+Rcqc/tVSiykfTG hC1LkCz6LnsA415CkwZ8ZIaFqMOobrs96slFKIu7ibZ5g5+hLvutHIetQQecLN64 Vg6ZbUf9AgMBAAECggEBAI4q66PQM2cj7kI4b6Q6l8sIvKBqYIr3MHL8v5CWkvuA XiQ7Hbd3af6NkZedL1iNs/eAz/iQkQbQOepoqFVjpE6w+OOFc9ejFmCvikZwSM8S YHTLstTp34Ux2u2WzmPYtAFwxQOfzM3sHyF0ZB269Xn+V65pMWJlRXhzqdmoR6B2 YYLzHVBZAbgj7IlS+f1WPkkbrb3BIs71tYyPS3Ng6nhGgXrnqmE5fhm93wR9KR5R RcgzFGH/8qD2LndmJz8tYtjvlq016Pp40VrYabIrvX6apglayShLQ+2XUe8VKH2Q dQPIKL3R0ZeDeqwn4PTMclBYd/nXFSbIQndufTDY84ECgYEA8RTcCPOFYkoDeQIr yKqWo1P1cKsB/bGzvLmo+FqFQij5lmj9iDdvpSQJ58Uj+TFy5qNRT6VueF8U4pNd Fmcbw05cH+9NzAXji6aYWCargbmhW4L+rzub+TQmdAfqXbBknVgrLenY4LFnFFm1 SIdJRid3vCwiRuhcjecg4FasR+kCgYEA0TTw4ZGxPCX5qt00PUFvuGpqFxFqZ7Ah wapwQTUY48WeC0Pg7Iq6ipnhjggkgYLkGc1klmTwJXKV0acIS5/DNclepMWZPwIc +GcKclQsy8MHm+L2tPGBQosRGiSrXi5LzBIQsYSyhBRDUSqu+yj7TTd2XqpUkLAw qdrWiIjVBvUCgYAOkh0uaVGBfEmzcZ8l1LGgE339HkjThX8AhBQjVo1BT2quXZAd QIR97ayvlmmzMPrp16sdbjk8Czse6pswtHCoID9PKs5/60cydJI2mbe58nc/Ka6s 9qRZrn44exX+LaAXJnINp1mVUwOQ5k8foBWcqNwCwoQb1wVpCjQhevuUqQKBgQC1 SA+3Fr0iprF6iqWastoxThzSEmhGowwNOjh9eJoxvOsfTdlYfzn3ojIeFhY0F4y6 gw03eQ3TFUCXZAq/JRhNwkl9tC//tkAOS5N00FXk1wH/5aLr1h2w4LqYEdBhEvLh SYInoRnjc3+FlNv9jVx9Y6Lxkt0mZ1YzyQp/UzptBQKBgFTOonik05rMHGlEg9Ng unjDAEK+7tXhM/RKxWDs7pZeGbdl1c/NDOX4wIbuK2cCkV0v/qfg6Ex2Obf5rqM4 R5lrs9y30rZbwmfms8G3cimTAsOcLqObKkBED+g07t+3bw44JXLwYLPm/z//bIcI fkxyxKL5ZJhP3/bPJJj8UAq3 -----END PRIVATE KEY----- `, SerialNo: global.GVA_CONFIG.Pays[0].SerialNo, // 商户支付证书序列号 NotifyURL: global.GVA_CONFIG.Pays[0].NotifyUrl, // 微信支付回调地址 HttpDebug: false, Log: payment.Log{ Level: "debug", // 可以重定向到你的目录下,如果设置File和Error,默认会在当前目录下的wechat文件夹下生成日志 File: "/Users/user/wechat/payment/info.log", Error: "/Users/user/wechat/payment/error.log", Stdout: false, // 是否打印在终端 }, Http: payment.Http{ Timeout: 30.0, BaseURI: "https://api.mch.weixin.qq.com", }, }) if err != nil { log.Println("初始化微信支付 SDK失败", err) } WechatPay = PaymentService } // H5Pay 发送订单 func H5Pay(order *app.Order, ctx *gin.Context) (interface{}, error) { options := &request.RequestH5Prepay{ Amount: &request.H5Amount{ Total: int(order.Price), Currency: "CNY", }, Attach: order.Desc, Description: order.Desc, OutTradeNo: order.OrderNo, // 这里是商户订单号,不能重复提交给微信 SceneInfo: &request.H5SceneInfo{ PayerClientIP: ctx.ClientIP(), // 用户终端IP H5Info: &request.H5H5Info{ Type: "Wap", AppName: "老陈课堂", }, }, } // 下单 response, err := WechatPay.Order.TransactionH5(ctx, options) if err != nil { global.GVA_LOG.Error("微信支付下单失败", zap.Error(err)) return "", err } /* payConf, err := WechatPay.JSSDK.BridgeConfig(response.PrepayID, true) if err != nil { global.GVA_LOG.Error("获取微信支付配置失败", zap.Error(err)) return "", err }*/ global.GVA_LOG.Info("微信支付配置", zap.Any("payConf", response)) return response, nil } func JSAPIPay(order *app.Order, ctx *gin.Context) (interface{}, error) { options := &request.RequestJSAPIPrepay{ Amount: &request.JSAPIAmount{ Total: int(order.Price), Currency: "CNY", }, Attach: order.Name, Description: order.Desc, OutTradeNo: order.OrderNo, // 这里是商户订单号,不能重复提交给微信 Payer: &request.JSAPIPayer{ OpenID: order.OpenId, // 用户的openid, 记得也是动态的。 }, } // 下单 response, err := WechatPay.Order.JSAPITransaction(ctx, options) if err != nil { panic(err) } // payConf大致如下: // { // "appId": "ww16143ea0101327c7", // "nonceStr": "jcMfo7lsfM3LPWkLRJb7rQU6WeatStEU", // "package": "prepay_id=xxxxx", // "paySign": "hSPF2wU0aYeTq+DJ14ELM...省略部分数据...RrOmlEkZXVxqCdZmniLdA==", // "signType": "RSA", // "timeStamp": "1634305101" // } payConf, err := WechatPay.JSSDK.BridgeConfig(response.PrepayID, true) if err != nil { global.GVA_LOG.Error("获取微信支付配置失败", zap.Error(err)) return "", err } return payConf, nil } // NotifyHandle 微信支付回调处理 func NotifyHandle(ctx *gin.Context) error { res, err := WechatPay.HandlePaidNotify( ctx.Request, func(message *wxRequst.RequestNotify, transaction *models.Transaction, fail func(message string)) interface{} { // 看下支付通知事件状态 // 这里可能是微信支付失败的通知,所以可能需要在数据库做一些记录,然后告诉微信我处理完成了。 if message.EventType != "TRANSACTION.SUCCESS" { fmt.Dump(transaction) return true } if transaction.OutTradeNo != "" { // 这里对照自有数据库里面的订单做查询以及支付状态改变 // 更新订单状态为已支付 order := app.Order{} err := global.GVA_DB.Model(&app.Order{}).Where("order_no = ?", transaction.OutTradeNo).First(&order).Error if err != nil { global.GVA_LOG.Error("查询订单失败", zap.Error(err)) return nil } order.Status = 2 // 设置订单状态为已支付 err = global.GVA_DB.Save(&order).Error if err != nil { global.GVA_LOG.Error("更新订单状态失败", zap.Error(err)) return nil } log.Printf("订单号:%s 支付成功", transaction.OutTradeNo) } else { // 因为微信这个回调不存在订单号,所以可以告诉微信我还没处理成功,等会它会重新发起通知 // 如果不需要,直接返回true即可 fail("payment fail") return nil } return true }, ) // 这里可能是因为不是微信官方调用的,无法正常解析出transaction和message,所以直接抛错。 if err != nil { res.StatusCode = 502 err = res.Write(ctx.Writer) return err //panic(err) } // 这里根据之前返回的是true或者fail,框架这边自动会帮你回复微信 err = res.Write(ctx.Writer) if err != nil { panic(err) } global.GVA_LOG.Info("微信支付回调处理成功", zap.Any("response", res)) return nil } func GenerateOrderNum() string { now := time.Now() dateStr := now.Format("060102") // 年的后2位+月+日 seconds := int(now.Sub(time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())).Seconds()) secondsStr := gfmt.Sprintf("%05d", seconds) nanoStr := gfmt.Sprintf("%06d", now.Nanosecond()/1000) // 微秒级,6位 rand.Seed(time.Now().UnixNano()) randStr := gfmt.Sprintf("%03d", rand.Intn(1000)) // 3位随机数 orderNo := gfmt.Sprintf("%s%s%s%s%s", "5258", dateStr, secondsStr, nanoStr, randStr) return orderNo }