目录
## 前言
前段时间开发AI助手网站,在网站核心功能、用户注册登录等模块开发完成后,最后一步就是支付。打通了在线支付就算是完成了整个开发闭环,几经尝试总算是搞定了,这里简单做个记录:
PS: 项目是vue+golang开发的
1.准备工作
1.1 网站域名
需要有个外网可访问的已备案域名,作为网站微信支付域名配置用。
1.2 微信公众号
需要在微信公众平台申请公众号,在基本配置中获取开发者ID(AppID)等参数
公众平台地址:https://mp.weixin.qq.com/
1.3 微信商户平台
需要在商户平台注册商户号,然后获取商户号、商户证书序列号、商户APIv3密钥等参数
微信商户平台地址:https://pay.weixin.qq.com/index.php/core/info
2.代码开发
2.1 生成微信支付二维码
2.1.1 选择套餐点击支付
前端核心功能代码--生成支付二维码:
//生成支付二维码
const genPayQrcode = () => {
loading.value = true
text.value = ""
httpPost("/api/payment/qrcode", {
pay_way: curPay.value,
product_id: curPayProduct.value.id,
user_id: user.value.id
}).then(res => {
showPayDialog.value = true
qrcode.value = res.data['url']
activeOrderNo.value = res.data['order_no']
queryOrder(activeOrderNo.value)
loading.value = false
// 重置计数器
if (countDownRef.value) {
countDownRef.value.resetTimer()
}
}).catch(e => {
ElMessage.error("生成支付订单失败:" + e.message)
})
}
2.1.2 后端核心代码
支付这里用的是github开源的wechatpay-apiv3/wechatpay-go
// Pay 执行支付请求操作 -Native支付
func (s *HuPiPayService) Pay(params HuPiPayReq) (HuPiResp, error) {
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
mchPrivateKey, err := utils2.LoadPrivateKeyWithPath(config.HuPiPayConfig.PrivateKeyWithPath)
if err != nil {
logger.Errorf("NewHuPiPay load merchant private key error")
}
ctx := context.Background()
// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
opts := []core.ClientOption{
option.WithWechatPayAutoAuthCipher(config.HuPiPayConfig.MchID, config.HuPiPayConfig.MchCertificateSerialNumber, mchPrivateKey, config.HuPiPayConfig.MchAPIv3Key),
}
client, err := core.NewClient(ctx, opts...)
if err != nil {
logger.Errorf("NewHuPiPay new wechat pay client err:%s", err)
}
ctx := context.Background()
// Native支付
svc := native.NativeApiService{Client: s.client}
// 发送请求
var res HuPiResp
logger.Infof("Pay params.TotalFee=%s", params.TotalFee)
totalFee, err := strconv.ParseInt(strings.Split(params.TotalFee, ".")[0], 10, 64)
logger.Infof("Pay totalFee=%d", totalFee)
if res.ErrCode != 0 {
logger.Errorf("HuPiPay Pay ParseInt err:%s", err)
return res, nil
}
resp, result, err := svc.Prepay(ctx,
native.PrepayRequest{
Appid: core.String(s.appId),
Mchid: core.String(s.mchID),
Description: core.String("小麦AI智能助手-会员充值"),
OutTradeNo: core.String(params.TradeOrderId),
Attach: core.String("自定义数据说明"),
NotifyUrl: core.String(s.notifyURL),
Amount: &native.Amount{
Total: core.Int64(totalFee),
},
},
)
// 使用微信扫描 resp.code_url 对应的二维码,即可体验Native支付
logger.Infof("Pay status=%d resp=%s", result.Response.StatusCode, resp)
res.URL = *resp.CodeUrl
return res, nil
}
最终生成的微信支付链接是这种格式:weixin://wxpay/bizpayurl?pr=REo4PlHz1,然后前端拿到支付链接通过qrcode以二维码样式展示,用户即可微信扫码下单:
DEMO 体验地址:https://ai.xiaomaicoder.com/login
2.2 扫码支付后,支付结果回调
支付结果回调地址NotifyUrl是发起支付的时候自己配置的,对应配置的域名需要和微信公众号配置的保持一致。
2.2.1 解析回调结果
这里用的是github开源的 go-pay/gopay
// WxPayNotify 微信支付异步回调
func (h *PaymentHandler) WxPayNotify(c *gin.Context) {
err := c.Request.ParseForm()
if err != nil {
c.String(http.StatusOK, "fail")
return
}
notifyReq, err := wechat.V3ParseNotify(c.Request)
if err != nil {
logger.Errorln("------ WxPayNotify V3ParseNotify ERR ------", err.Error())
c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.FAIL, Message: "回调内容异常"})
return
}
logger.Infof("WxPayNotify 返回值notifyReq: %+v\n", notifyReq)
// 普通支付通知解密
result, rErr := notifyReq.DecryptCipherText(h.App.Config.HuPiPayConfig.MchAPIv3Key)
if rErr != nil {
logger.Errorln("------ WxPayNotify DecryptCipherText Error ------", rErr.Error())
c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.FAIL, Message: "内容解密失败"})
return
}
logger.Infof("返回值result: %+v\n", result)
if result != nil && result.TradeState == "SUCCESS" {
logger.Infof("------ WxPayNotify PushMessToPayQueue START 【" + result.OutTradeNo + "】------")
orderNo := result.OutTradeNo
tradeNo := result.TransactionId
logger.Infof("收到微信订单支付回调,订单 NO:%s,交易流水号:%s", orderNo, tradeNo)
//更新订单等具体业务逻辑
err = h.notify(orderNo, tradeNo)
if err != nil {
c.String(http.StatusOK, "fail")
return
}
c.String(http.StatusOK, "success")
logger.Infof("------ WxPayNotify PushMessToPayQueue END 【" + result.OutTradeNo + "】------")
}
logger.Infof("--------------------- WxPayNotify END ---------------------")
}
2.2.2 更新订单数据
根据具体业务逻辑处理
2.2.3 更新用户算力值
根据具体业务逻辑处理
2.3 页面同步查询支付回调结果
2.3.1 查到结果后实时同步支付状态
2.3.2 支付成功后给出提示并刷新页面
//查询订单支付状态
const queryOrder = (orderNo) => {
httpPost("/api/payment/query", {order_no: orderNo}).then(res => {
if (res.data.status === 1) {
text.value = "扫码成功,请在手机上进行支付!"
queryOrder(orderNo)
} else if (res.data.status === 2) {
text.value = "支付成功,正在刷新页面"
if (curPay.value === "payjs") {
setTimeout(() => location.reload(), 3000)
} else {
setTimeout(() => location.reload(), 500)
}
} else {
// 如果当前订单没有过期,继续等待订单的下一个状态
if (activeOrderNo.value === orderNo) {
queryOrder(orderNo)
}
}
}).catch(e => {
ElMessage.error("查询支付状态失败:" + e.message)
})
}
## 后记
至此PC端微信Native扫码支付开发工作已完成,调试功能OK即可。
最后提一下自己的小站(支持微信一键登录),提供了chatGPT等多种AI对话及MJ绘画功能。
体验地址:https://ai.xiaomaicoder.com/login,欢迎体验交流!