微信相关能力

This commit is contained in:
2026-01-30 17:25:30 +08:00
parent 4856a88d41
commit bf591095a2
43 changed files with 4297 additions and 391 deletions

View File

@@ -3,6 +3,7 @@
package app
import (
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/internal/service/personal_customer"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/response"
@@ -108,27 +109,49 @@ func (h *PersonalCustomerHandler) Login(c *fiber.Ctx) error {
return response.Success(c, resp)
}
// BindWechatRequest 绑定微信请求
type BindWechatRequest struct {
Code string `json:"code" validate:"required"` // 微信授权码
}
// BindWechat 绑定微信
// POST /api/c/v1/bind-wechat
// TODO: 实现微信 OAuth 授权逻辑
func (h *PersonalCustomerHandler) BindWechat(c *fiber.Ctx) error {
var req BindWechatRequest
// WechatOAuthLogin 微信 OAuth 登录
// POST /api/c/v1/wechat/auth
func (h *PersonalCustomerHandler) WechatOAuthLogin(c *fiber.Ctx) error {
var req dto.WechatOAuthRequest
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
// TODO: 实现完整的微信绑定流程
// 1. 从 context 中获取当前登录的客户 ID
// 2. 使用微信授权码换取 OpenID 和 UnionID
// 3. 调用 service 层的 BindWechat 方法绑定微信
result, err := h.service.WechatOAuthLogin(c.Context(), req.Code)
if err != nil {
h.logger.Error("微信 OAuth 登录失败",
zap.String("code", req.Code),
zap.Error(err),
)
return err
}
return response.Success(c, result)
}
// BindWechat 绑定微信
// POST /api/c/v1/bind-wechat
func (h *PersonalCustomerHandler) BindWechat(c *fiber.Ctx) error {
var req dto.WechatOAuthRequest
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
customerID, ok := c.Locals("customer_id").(uint)
if !ok {
return errors.New(errors.CodeUnauthorized, "未找到客户信息")
}
if err := h.service.BindWechatWithCode(c.Context(), customerID, req.Code); err != nil {
h.logger.Error("绑定微信失败",
zap.Uint("customer_id", customerID),
zap.Error(err),
)
return err
}
return response.Success(c, fiber.Map{
"message": "微信绑定功能暂未实现,待微信 SDK 对接后启用",
"message": "绑定成功",
})
}

View File

@@ -1,38 +1,51 @@
package callback
import (
"context"
"net/http"
"github.com/gofiber/fiber/v2"
"github.com/valyala/fasthttp/fasthttpadaptor"
"github.com/break/junhong_cmp_fiber/internal/model"
orderService "github.com/break/junhong_cmp_fiber/internal/service/order"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/response"
"github.com/break/junhong_cmp_fiber/pkg/wechat"
)
type PaymentHandler struct {
orderService *orderService.Service
orderService *orderService.Service
wechatPayment wechat.PaymentServiceInterface
}
func NewPaymentHandler(orderService *orderService.Service) *PaymentHandler {
return &PaymentHandler{orderService: orderService}
}
type WechatPayCallbackRequest struct {
OrderNo string `json:"order_no" xml:"out_trade_no"`
func NewPaymentHandler(orderService *orderService.Service, wechatPayment wechat.PaymentServiceInterface) *PaymentHandler {
return &PaymentHandler{
orderService: orderService,
wechatPayment: wechatPayment,
}
}
// WechatPayCallback 微信支付回调(带签名验证)
// POST /api/callback/wechat-pay
func (h *PaymentHandler) WechatPayCallback(c *fiber.Ctx) error {
var req WechatPayCallbackRequest
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
if h.wechatPayment == nil {
return errors.New(errors.CodeWechatCallbackInvalid, "微信支付服务未配置")
}
if req.OrderNo == "" {
return errors.New(errors.CodeInvalidParam, "订单号不能为空")
}
var httpReq http.Request
fasthttpadaptor.ConvertRequest(c.Context(), &httpReq, true)
if err := h.orderService.HandlePaymentCallback(c.UserContext(), req.OrderNo, model.PaymentMethodWechat); err != nil {
return err
ctx := context.Background()
_, err := h.wechatPayment.HandlePaymentNotify(&httpReq, func(result *wechat.PaymentNotifyResult) error {
if result.TradeState != "SUCCESS" {
return nil
}
return h.orderService.HandlePaymentCallback(ctx, result.OutTradeNo, model.PaymentMethodWechat)
})
if err != nil {
return errors.Wrap(errors.CodeWechatCallbackInvalid, err, "处理微信支付回调失败")
}
return response.Success(c, map[string]string{"return_code": "SUCCESS"})

View File

@@ -129,3 +129,79 @@ func (h *OrderHandler) WalletPay(c *fiber.Ctx) error {
return response.Success(c, nil)
}
// WechatPayJSAPI 微信 JSAPI 支付
// POST /api/h5/orders/:id/wechat-pay/jsapi
func (h *OrderHandler) WechatPayJSAPI(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
if err != nil {
return errors.New(errors.CodeInvalidParam, "无效的订单ID")
}
var req dto.WechatPayJSAPIRequest
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
ctx := c.UserContext()
userType := middleware.GetUserTypeFromContext(ctx)
var buyerType string
var buyerID uint
switch userType {
case constants.UserTypeAgent:
buyerType = model.BuyerTypeAgent
buyerID = middleware.GetShopIDFromContext(ctx)
case constants.UserTypePersonalCustomer:
buyerType = model.BuyerTypePersonal
buyerID = middleware.GetCustomerIDFromContext(ctx)
default:
return errors.New(errors.CodeForbidden, "不支持的用户类型")
}
result, err := h.service.WechatPayJSAPI(ctx, uint(id), req.OpenID, buyerType, buyerID)
if err != nil {
return err
}
return response.Success(c, result)
}
// WechatPayH5 微信 H5 支付
// POST /api/h5/orders/:id/wechat-pay/h5
func (h *OrderHandler) WechatPayH5(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
if err != nil {
return errors.New(errors.CodeInvalidParam, "无效的订单ID")
}
var req dto.WechatPayH5Request
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
ctx := c.UserContext()
userType := middleware.GetUserTypeFromContext(ctx)
var buyerType string
var buyerID uint
switch userType {
case constants.UserTypeAgent:
buyerType = model.BuyerTypeAgent
buyerID = middleware.GetShopIDFromContext(ctx)
case constants.UserTypePersonalCustomer:
buyerType = model.BuyerTypePersonal
buyerID = middleware.GetCustomerIDFromContext(ctx)
default:
return errors.New(errors.CodeForbidden, "不支持的用户类型")
}
result, err := h.service.WechatPayH5(ctx, uint(id), &req.SceneInfo, buyerType, buyerID)
if err != nil {
return err
}
return response.Success(c, result)
}