🎨 完善订单和微信支付功能
This commit is contained in:
245
utils/wechat/pay.go
Normal file
245
utils/wechat/pay.go
Normal file
@@ -0,0 +1,245 @@
|
||||
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
|
||||
}
|
@@ -3,15 +3,12 @@ package wechat
|
||||
import (
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"github.com/ArtisanCloud/PowerSocialite/v3/src/providers"
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount"
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/payment"
|
||||
"go.uber.org/zap"
|
||||
"log"
|
||||
)
|
||||
|
||||
var WeOfficial *officialAccount.OfficialAccount
|
||||
var WechatPay *payment.Payment
|
||||
|
||||
// InitWeOfficial 初始化微信公众号
|
||||
func InitWeOfficial() {
|
||||
@@ -37,43 +34,6 @@ func InitWeOfficial() {
|
||||
WeOfficial = OfficialAccountApp
|
||||
}
|
||||
|
||||
// InitWechatPay 初始化微信支付
|
||||
func InitWechatPay() {
|
||||
PaymentService, err := payment.NewPayment(&payment.UserConfig{
|
||||
AppID: "wx3d21df18d7f8f9fc\n", // 小程序、公众号或者企业微信的appid
|
||||
MchID: "[mch_id]", // 商户号 appID
|
||||
MchApiV3Key: "1a3sd8561d5179Df152D4789aD38wG9s", // 微信V3接口调用必填
|
||||
Key: "57s14dFG915486Sd5617f23d45f671Ad", // 微信V2接口调用必填
|
||||
CertPath: "[wx_cert_path]", // 商户后台支付的Cert证书路径
|
||||
KeyPath: "[wx_key_path]", // 商户后台支付的Key证书路径
|
||||
SerialNo: "[serial_no]", // 商户支付证书序列号
|
||||
NotifyURL: "[notify_url]", // 微信支付回调地址
|
||||
HttpDebug: true,
|
||||
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",
|
||||
},
|
||||
// 可选,不传默认走程序内存
|
||||
Cache: kernel.NewRedisClient(&kernel.UniversalOptions{
|
||||
Addrs: []string{"127.0.0.1:6379"},
|
||||
Password: "",
|
||||
DB: 0,
|
||||
}),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Println("初始化微信支付 SDK失败", err)
|
||||
}
|
||||
WechatPay = PaymentService
|
||||
}
|
||||
|
||||
func GetUserInfo(code string) *providers.User {
|
||||
userFromCode, err := WeOfficial.OAuth.UserFromCode(code)
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user