f
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -24,6 +24,7 @@ data/*
|
|||||||
/app/main/api/_*
|
/app/main/api/_*
|
||||||
__debug_bin.exe
|
__debug_bin.exe
|
||||||
**/.../
|
**/.../
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
|
||||||
# 文档目录
|
# 文档目录
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ service main {
|
|||||||
|
|
||||||
@handler PaymentCheck
|
@handler PaymentCheck
|
||||||
post /pay/check (PaymentCheckReq) returns (PaymentCheckResp)
|
post /pay/check (PaymentCheckReq) returns (PaymentCheckResp)
|
||||||
|
|
||||||
|
// 小程序虚拟支付:上报微信/Apple 客户端提示与步骤(写入服务端日志并可选查微信单)
|
||||||
|
@handler XpayClientEvent
|
||||||
|
post /pay/xpay/client-event (XpayClientEventReq) returns (XpayClientEventResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -69,6 +73,23 @@ type (
|
|||||||
WxOrderStatus int `json:"wx_order_status,optional"` // 微信侧订单 status
|
WxOrderStatus int `json:"wx_order_status,optional"` // 微信侧订单 status
|
||||||
WxOrderDetail string `json:"wx_order_detail,optional"` // query_order 原始 order 摘要(排查用)
|
WxOrderDetail string `json:"wx_order_detail,optional"` // query_order 原始 order 摘要(排查用)
|
||||||
}
|
}
|
||||||
|
XpayClientEventReq {
|
||||||
|
OrderNo string `json:"order_no,optional"`
|
||||||
|
Stage string `json:"stage" validate:"required"` // prepay_ok, invoke, success, fail, check
|
||||||
|
EventType string `json:"event_type" validate:"required,oneof=info tip fail success"`
|
||||||
|
Message string `json:"message,optional"` // 微信/Apple 展示给用户的话术(非 HTTP 报错)
|
||||||
|
ErrMsg string `json:"err_msg,optional"`
|
||||||
|
ErrCode int `json:"err_code,optional"`
|
||||||
|
Errno int `json:"errno,optional"`
|
||||||
|
SignData string `json:"sign_data,optional"`
|
||||||
|
Device string `json:"device,optional"`
|
||||||
|
Extra string `json:"extra,optional"`
|
||||||
|
}
|
||||||
|
XpayClientEventResp {
|
||||||
|
WxOrderStatus int `json:"wx_order_status,optional"`
|
||||||
|
WxOrderDetail string `json:"wx_order_detail,optional"`
|
||||||
|
WxSyncError string `json:"wx_sync_error,optional"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
30
app/main/api/internal/handler/pay/xpayclienteventhandler.go
Normal file
30
app/main/api/internal/handler/pay/xpayclienteventhandler.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package pay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"qnc-server/app/main/api/internal/logic/pay"
|
||||||
|
"qnc-server/app/main/api/internal/svc"
|
||||||
|
"qnc-server/app/main/api/internal/types"
|
||||||
|
"qnc-server/common/result"
|
||||||
|
"qnc-server/pkg/lzkit/validator"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func XpayClientEventHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.XpayClientEventReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
result.ParamErrorResult(r, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := validator.Validate(req); err != nil {
|
||||||
|
result.ParamValidateErrorResult(r, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l := pay.NewXpayClientEventLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.XpayClientEvent(&req)
|
||||||
|
result.HttpResult(r, w, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -976,6 +976,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
Path: "/pay/payment",
|
Path: "/pay/payment",
|
||||||
Handler: pay.PaymentHandler(serverCtx),
|
Handler: pay.PaymentHandler(serverCtx),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/pay/xpay/client-event",
|
||||||
|
Handler: pay.XpayClientEventHandler(serverCtx),
|
||||||
|
},
|
||||||
}...,
|
}...,
|
||||||
),
|
),
|
||||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||||
|
|||||||
65
app/main/api/internal/logic/pay/xpayclienteventlogic.go
Normal file
65
app/main/api/internal/logic/pay/xpayclienteventlogic.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package pay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"qnc-server/app/main/api/internal/svc"
|
||||||
|
"qnc-server/app/main/api/internal/types"
|
||||||
|
"qnc-server/common/ctxdata"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type XpayClientEventLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewXpayClientEventLogic(ctx context.Context, svcCtx *svc.ServiceContext) *XpayClientEventLogic {
|
||||||
|
return &XpayClientEventLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XpayClientEvent 记录小程序虚拟支付各步骤及微信/Apple 客户端提示(非 HTTP 异常)
|
||||||
|
func (l *XpayClientEventLogic) XpayClientEvent(req *types.XpayClientEventReq) (*types.XpayClientEventResp, error) {
|
||||||
|
userID, _ := ctxdata.GetUidFromCtx(l.ctx)
|
||||||
|
|
||||||
|
l.Infof("[xpay:client-event] user=%s order_no=%s stage=%s event=%s message=%q err_msg=%q err_code=%d errno=%d sign_data=%s device=%s extra=%s",
|
||||||
|
userID, req.OrderNo, req.Stage, req.EventType, req.Message, req.ErrMsg, req.ErrCode, req.Errno,
|
||||||
|
req.SignData, req.Device, req.Extra)
|
||||||
|
|
||||||
|
resp := &types.XpayClientEventResp{}
|
||||||
|
|
||||||
|
if req.OrderNo == "" || l.svcCtx.XpayService == nil || !l.svcCtx.XpayService.Enabled() {
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户端 fail/tip 时主动 query_order,把微信服务端侧状态一并记入日志
|
||||||
|
if req.EventType == "fail" || req.EventType == "tip" {
|
||||||
|
openid, err := l.svcCtx.XpayService.GetWxMiniOpenID(l.ctx, l.svcCtx.UserAuthModel, userID)
|
||||||
|
if err != nil {
|
||||||
|
resp.WxSyncError = "获取 openid 失败: " + err.Error()
|
||||||
|
l.Errorf("[xpay:client-event] query_order skip order_no=%s reason=%v", req.OrderNo, err)
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
status, qErr := l.svcCtx.XpayService.QueryOrder(l.ctx, openid, req.OrderNo, "")
|
||||||
|
if qErr != nil {
|
||||||
|
resp.WxSyncError = qErr.Error()
|
||||||
|
l.Errorf("[xpay:client-event] query_order FAIL order_no=%s client_msg=%q wx_err=%v",
|
||||||
|
req.OrderNo, req.Message, qErr)
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.WxOrderStatus = status.Status
|
||||||
|
resp.WxOrderDetail = status.RawOrder
|
||||||
|
l.Infof("[xpay:client-event] query_order SNAPSHOT order_no=%s client_msg=%q wx_status=%d wx_order_id=%s paid_fee=%d err_msg=%s raw=%s",
|
||||||
|
req.OrderNo, req.Message, status.Status, status.WxOrderID, status.PaidFee, status.ErrMsg, status.RawOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"qnc-server/app/main/api/internal/service"
|
"qnc-server/app/main/api/internal/service"
|
||||||
"qnc-server/app/main/api/internal/svc"
|
"qnc-server/app/main/api/internal/svc"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -61,6 +62,9 @@ func (l *XpayPushLogic) HandlePOST(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
logx.WithContext(ctx).Infof("[xpay:push] recv event=%s order_no=%s openid=%s env=%d body=%s",
|
||||||
|
notify.Event, orderNo, notify.OpenId, notify.Env, string(body))
|
||||||
|
|
||||||
already, _ := l.svcCtx.XpayService.AlreadyNotified(ctx, orderNo)
|
already, _ := l.svcCtx.XpayService.AlreadyNotified(ctx, orderNo)
|
||||||
if already {
|
if already {
|
||||||
l.writePushResp(w, 0, "already notified")
|
l.writePushResp(w, 0, "already notified")
|
||||||
|
|||||||
@@ -1935,6 +1935,25 @@ type PaymentCheckResp struct {
|
|||||||
WxOrderDetail string `json:"wx_order_detail,optional"` // query_order 原始 order 摘要(排查用)
|
WxOrderDetail string `json:"wx_order_detail,optional"` // query_order 原始 order 摘要(排查用)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type XpayClientEventReq struct {
|
||||||
|
OrderNo string `json:"order_no,optional"`
|
||||||
|
Stage string `json:"stage" validate:"required"` // prepay_ok, invoke, success, fail, check
|
||||||
|
EventType string `json:"event_type" validate:"required,oneof=info tip fail success"`
|
||||||
|
Message string `json:"message,optional"` // 微信/Apple 展示给用户的话术
|
||||||
|
ErrMsg string `json:"err_msg,optional"`
|
||||||
|
ErrCode int `json:"err_code,optional"`
|
||||||
|
Errno int `json:"errno,optional"`
|
||||||
|
SignData string `json:"sign_data,optional"`
|
||||||
|
Device string `json:"device,optional"`
|
||||||
|
Extra string `json:"extra,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type XpayClientEventResp struct {
|
||||||
|
WxOrderStatus int `json:"wx_order_status,optional"`
|
||||||
|
WxOrderDetail string `json:"wx_order_detail,optional"`
|
||||||
|
WxSyncError string `json:"wx_sync_error,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
type PaymentReq struct {
|
type PaymentReq struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
PayMethod string `json:"pay_method"` // 支付方式: wechat, alipay, appleiap, test(仅开发环境), test_empty(仅开发环境-空报告模式)
|
PayMethod string `json:"pay_method"` // 支付方式: wechat, alipay, appleiap, test(仅开发环境), test_empty(仅开发环境-空报告模式)
|
||||||
|
|||||||
Reference in New Issue
Block a user