feat: 微信支付错误提示,跳转return

This commit is contained in:
wolter 2024-12-07 14:35:43 +08:00
parent cacfbccbcf
commit 964a0808b9
11 changed files with 335 additions and 74 deletions

View File

@ -77,6 +77,7 @@ const (
OrderNotSupportRefundPart = 1413
OrderRefundAmountError = 1414
OrderPayChannelChange = 1415
OrderPayRequestLogNotExist = 1416
// 关闭订单
CloseOrderPayed = 1420
@ -159,15 +160,16 @@ var MsgZH = map[int]string{
RequestLogNotFound: "未找到日志信息",
RequestResponseValid: "上游返回格式无效",
OrdersNotFound: "未找到订单",
OrdersExist: "订单已存在",
OrderTypeNotFount: "未知的支付方式",
OrderIsDelete: "订单已删除",
OrderStatusErr: "订单状态错误",
OrderClosed: "订单已关闭,无法继续支付",
OrderFailed: "订单支付失败,请重新发起",
OrderPayed: "订单已支付成功,请勿重复支付",
OrderPayChannelChange: "支付方式不一致,切换支付方式请关闭原订单生成新订单",
OrdersNotFound: "未找到订单",
OrdersExist: "订单已存在",
OrderTypeNotFount: "未知的支付方式",
OrderIsDelete: "订单已删除",
OrderStatusErr: "订单状态错误",
OrderClosed: "订单已关闭,无法继续支付",
OrderFailed: "订单支付失败,请重新发起",
OrderPayed: "订单已支付成功,请勿重复支付",
OrderPayChannelChange: "支付方式不一致,切换支付方式请关闭原订单生成新订单",
OrderPayRequestLogNotExist: "订单下单数据不存在",
NotifySendFail: "回调发送失败",
RefundOrderNotFound: "退款订单未找到",

View File

@ -49,3 +49,8 @@ func (m *OrderRequestLogRepo) OrderRequestLogBackendList(conn builder.Cond, page
repo.Join("left", "orders", "orders.app_id = order_request_log.app_id and orders.out_trade_no = order_request_log.out_trade_no")
return repo.Desc("order_request_log.create_time").FindAndCount(orderLogList)
}
// 查询订单日志
func (m *OrderRequestLogRepo) OrderRequestLogGet(orderLog *orderrequestlogmodel.OrderRequestLog, conn builder.Cond) (bool, error) {
return m.repo.Where(conn).Get(orderLog)
}

View File

@ -9,6 +9,7 @@ import (
"PaymentCenter/app/services"
"PaymentCenter/app/services/thirdpay"
"PaymentCenter/app/third/paymentService"
"PaymentCenter/app/utils"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
@ -193,11 +194,6 @@ func BrokerWechatUrl(c *gin.Context) {
}
}
// 首页
func Index(c *gin.Context) {
c.HTML(200, "index.html", gin.H{})
}
// 拼接微信授权链接
func GetWxAuthUrl(c *gin.Context) {
req, _ := controllers.GetRequest(c).(*front.GetWxAuthUrlRequest)
@ -222,10 +218,12 @@ func GetWxCode(c *gin.Context) {
// 微信JSAPI 支付接口
/*
预支付接口实现如下功能
1.订单请求参数
2.订单信息存数据
3.返回用户授权地址
4.用户授权后获取code
wxjsapi接口实现如下功能
4.用户授权后获取codestatu传的是订单号
5.通过code获取openid
6.通过openid请求微信下单返回支付参数
7.支付参数聚合签名返回支付参数给前端
@ -236,7 +234,7 @@ func WxJsApiPay(c *gin.Context) {
rsp, code := thirdpay.WxJsApiPay(*req)
if code == errorcode.Success {
c.HTML(http.StatusOK, "index.html", gin.H{
c.HTML(http.StatusOK, "success.html", gin.H{
"appId": rsp.AppId,
"timeStamp": rsp.TimeStamp,
"nonceStr": rsp.NonceStr,
@ -245,6 +243,19 @@ func WxJsApiPay(c *gin.Context) {
"paySign": rsp.PaySign,
})
} else {
controllers.ApiRes(c, rsp, code, rsp.ThirdMsg)
utils.Log(c, "", "WxJsApiPay,支付失败code=", code, "msg=", rsp.ThirdMsg)
errmsg := rsp.ThirdMsg
data := make(map[string]interface{})
_ = json.Unmarshal([]byte(rsp.ThirdMsg), &data)
if message, ok := data["message"]; ok {
errmsg = message.(string)
}
if errmsg == "" {
errmsg = errorcode.GetMsg(code, "")
}
c.HTML(http.StatusOK, "fail.html", gin.H{
"errmsg": errmsg,
})
}
}

View File

@ -71,12 +71,12 @@ func RegisterRoute(router *gin.Engine) {
router.LoadHTMLGlob("./front/templates/*")
wx := v1.Group("/wx", middlewares.ValidateRequest())
{
wx.GET("/index", front.Index) // 获取页面
wx.POST("/getWxAuthUrl", front.GetWxAuthUrl) // 获取授权codeUrl
wx.GET("/getWxAuth", front.GetWxAuth) // 获取openId
wx.GET("/getCode", front.GetWxCode) // 接收微信code
}
// 微信jsapi支付链接
router.GET(common.WXCodeRedirectAddress, middlewares.ValidateRequest(), front.WxJsApiPay)
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

View File

@ -149,3 +149,17 @@ func PayOrderCheckRepeat(order *ordersmodel.Orders, conn builder.Cond) (exist bo
}
return true, code
}
// 商户请求日志
func OrderRequestLogs(req *orderrequestlogmodel.OrderRequestLog) (bool, error) {
requestRepo := data.NewOrderRequestLogRepo(paychannelmodel.GetInstance().GetDb())
conn := builder.NewCond()
if req.OutTradeNo != "" {
conn = conn.And(builder.Eq{"out_trade_no": req.OutTradeNo})
}
if req.AppId > 0 {
conn = conn.And(builder.Eq{"app_id": req.AppId})
}
return requestRepo.OrderRequestLogGet(req, conn)
}

View File

@ -129,9 +129,15 @@ func (w *Pay) PayUrl() (url string) {
case common.PAY_CHANNEL_WECHAT_JSAPI:
// 获取授权链接
res.Result, res.Code = w.GetWxAuthUrl(w.PayParam.Channel.AppId, w.Order.Id)
case common.PAY_CHANNEL_ALIPAY_WEB | common.PAY_CHANNEL_WECHAT_H5:
case common.PAY_CHANNEL_ALIPAY_WEB:
// 支付宝h5
res = paymentService.PaymentService(*w.ctx, *thirdPay)
case common.PAY_CHANNEL_WECHAT_H5:
// 微信h5支付
res = paymentService.PaymentService(*w.ctx, *thirdPay)
if res.Result != "" {
res.Result = config.GetConf().PayService.Host + "/pay/front/api/v1/brokerWechatUrl" + "?url=" + res.Result
}
default:
w.PayCode = errorcode.PayChannelNotBuild
return

View File

@ -4,6 +4,7 @@ import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/http/entities/front"
"PaymentCenter/app/models/orderrequestlogmodel"
"PaymentCenter/app/models/ordersmodel"
"PaymentCenter/app/models/paychannelmodel"
"PaymentCenter/app/services"
@ -127,12 +128,13 @@ func GetWxAuth(param front.GetWxAuthRequest) (rsp front.GetWxAuthResponse, code
7.支付参数聚合签名返回支付参数给前端
*/
type wxJsapiPay struct {
code int
msg string
param front.WxJsApiPayRequest
order *ordersmodel.Orders
payChannel paychannelmodel.PayChannel
wxConfig paymentService.WxPay
code int
msg string
param front.WxJsApiPayRequest
order *ordersmodel.Orders
payChannel paychannelmodel.PayChannel
wxConfig paymentService.WxPay
orderPayRequest front.PayReqs
openId string // 用户openid
}
@ -144,6 +146,15 @@ func newWxJsapiPay(param front.WxJsApiPayRequest) *wxJsapiPay {
func (this *wxJsapiPay) getOrder() {
order := new(ordersmodel.Orders)
this.order, this.code = services.OrderFindOne(order, builder.Eq{"id": this.param.State})
if this.code != errorcode.Success {
return
}
switch this.order.Status {
case common.ORDER_STATUS_PAYED:
this.code = errorcode.OrderPayed
case common.ORDER_STATUS_CLOSE:
this.code = errorcode.OrderClosed
}
}
func (this *wxJsapiPay) getPayChannel() {
payChannel := paychannelmodel.PayChannel{Id: this.order.PayChannelId}
@ -151,6 +162,29 @@ func (this *wxJsapiPay) getPayChannel() {
this.payChannel = payChannel
}
// 获取下单的请求参数
func (this *wxJsapiPay) getOrderRequestLogs() {
orderRequestLog := orderrequestlogmodel.OrderRequestLog{
OutTradeNo: this.order.OutTradeNo,
AppId: this.order.AppId,
}
has, err := services.OrderRequestLogs(&orderRequestLog)
if err != nil {
this.code = handErr(err)
return
}
if !has {
this.code = errorcode.OrderPayRequestLogNotExist
return
}
err = json.Unmarshal([]byte(orderRequestLog.MerchantRequest), &this.orderPayRequest)
if err != nil {
this.code = handErr(err)
return
}
}
func (this *wxJsapiPay) getOpenId() {
var err error
//// 配置支付的解析
@ -251,7 +285,11 @@ func WxJsApiPay(param front.WxJsApiPayRequest) (response front.WxJsApiPayRespons
if task.code != errorcode.Success {
return response, task.code
}
// 获取下单的请求参数
task.getOrderRequestLogs()
if task.code != errorcode.Success {
return response, task.code
}
// 2 通过code获取openid
task.getOpenId()
if task.code != errorcode.Success {
@ -269,7 +307,7 @@ func WxJsApiPay(param front.WxJsApiPayRequest) (response front.WxJsApiPayRespons
Description: order.Desc,
Amount: order.Amount,
PayerClientIp: param.ClientIp,
ReturnUrl: "",
ReturnUrl: task.orderPayRequest.ReturnUrl,
Wx: task.wxConfig,
Ali: paymentService.AliPay{},
OpenId: task.openId,

View File

@ -129,8 +129,8 @@ func WxJsApiPayInfo(c context.Context, payOrderRequest PayOrderRequest) (string,
return "", err
}
if wxRsp.Code != wechat.Success || wxRsp.Response.PrepayId == "" {
//logger.Error(c, "WxJsApiPayInfo 发生错误", fmt.Sprintf("错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
return "", errors.New(fmt.Sprintf("发起支付失败,失败状态码:%d, 失败原因:%s", wxRsp.Code, wxRsp.Error))
logger.Error(c, "WxJsApiPayInfo 发生错误", fmt.Sprintf("错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error))
return "", errors.New(wxRsp.Error)
}
//if payOrderRequest.ReturnUrl != "" {
// values := url.Values{}

99
front/templates/fail.html Normal file
View File

@ -0,0 +1,99 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>微信支付</title>
<style>
body,
html {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.success,
.error {
width: 100%;
height: 100%;
display: none;
background: #fff;
}
#right {
width: 150px;
height: 150px;
margin: 100px auto 20px;
border-radius: 50%;
border: 5px solid rgb(104, 225, 52);
display: flex;
justify-content: center;
align-items: center;
}
#right::before {
content: "";
display: block;
width: 88px;
height: 50px;
border: 5px solid rgb(104, 225, 52);
border-right: none;
border-top: none;
transform: rotate(-45deg) translate(7px, -10px);
}
.success-txt {
display: block;
width: 100%;
text-align: center;
font-size: 18px;
color: rgb(104, 225, 52);
}
#error {
width: 150px;
height: 150px;
margin: 100px auto 20px;
border-radius: 50%;
border: 5px solid rgb(244, 57, 52);
display: flex;
justify-content: center;
align-items: center;
}
#error::before {
content: "\2715";
display: block;
color: rgb(244, 57, 52);
font-size: 100px;
}
.error-txt {
display: block;
width: 100%;
text-align: center;
font-size: 18px;
color: rgb(244, 57, 52);
}
</style>
</head>
<body>
<div class="success">
<span id="right"></span>
<span class="success-txt">支付成功</span>
</div>
<div class="error">
<span id="error"></span>
<span class="error-txt">支付失败 {{.errmsg}}</span>
</div>
<script>
document.getElementsByClassName('error')[0].style.display = 'block';
document.getElementsByClassName('success')[0].style.display = 'none';
document.getElementsByClassName('error')[0].innerHTML = res.err_msg || '支付失败';
</script>
</body>
</html>

View File

@ -1,44 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>微信支付</title>
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>
<body>
<script>
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": "{{.appId}}", //公众号ID由商户传入
"timeStamp": "{{.timeStamp}}", //时间戳自1970年以来的秒数
"nonceStr": "{{.nonceStr}}", //随机串
"package": "{{.package}}",
"signType": "{{.signType}}", //微信签名方式:
"paySign": "{{.paySign}}" //微信签名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok但并不保证它绝对可靠。
alert('支付成功');
} else {
alert('支付失败');
}
});
}
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
</script>
</body>
</html>

View File

@ -0,0 +1,130 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>微信支付</title>
<style>
body,
html {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.success,
.error {
width: 100%;
height: 100%;
display: none;
background: #fff;
}
#right {
width: 150px;
height: 150px;
margin: 100px auto 20px;
border-radius: 50%;
border: 5px solid rgb(104, 225, 52);
display: flex;
justify-content: center;
align-items: center;
}
#right::before {
content: "";
display: block;
width: 88px;
height: 50px;
border: 5px solid rgb(104, 225, 52);
border-right: none;
border-top: none;
transform: rotate(-45deg) translate(7px, -10px);
}
.success-txt {
display: block;
width: 100%;
text-align: center;
font-size: 18px;
color: rgb(104, 225, 52);
}
#error {
width: 150px;
height: 150px;
margin: 100px auto 20px;
border-radius: 50%;
border: 5px solid rgb(244, 57, 52);
display: flex;
justify-content: center;
align-items: center;
}
#error::before {
content: "\2715";
display: block;
color: rgb(244, 57, 52);
font-size: 100px;
}
.error-txt {
display: block;
width: 100%;
text-align: center;
font-size: 18px;
color: rgb(244, 57, 52);
}
</style>
</head>
<body>
<div class="success">
<span id="right"></span>
<span class="success-txt">支付成功</span>
</div>
<div class="error">
<span id="error"></span>
<span class="error-txt">支付失败</span>
</div>
<script>
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": "{{.appId}}",
"timeStamp": "{{.timeStamp}}",
"nonceStr": "{{.nonceStr}}",
"package": "{{.package}}",
"signType": "{{.signType}}",
"paySign": "{{.paySign}}"
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok但并不保证它绝对可靠。
document.getElementsByClassName('success')[0].style.display = 'block';
document.getElementsByClassName('error')[0].style.display = 'none';
} else {
document.getElementsByClassName('error')[0].style.display = 'block';
document.getElementsByClassName('success')[0].style.display = 'none';
document.getElementsByClassName('error')[0].innerHTML = res.err_msg || '支付失败';
}
});
}
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
</script>
</body>
</html>