qnc-server-old/api/pay.go
2024-12-25 11:59:33 +08:00

763 lines
26 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package api
import (
"context"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
"github.com/smartwalle/alipay/v3"
wxcore "github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
wxutils "github.com/wechatpay-apiv3/wechatpay-go/utils"
"gorm.io/gorm"
"log"
"math"
"net/http"
"qnc-server/config"
"qnc-server/db"
"qnc-server/global"
"qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/service"
"qnc-server/utils"
"strconv"
"time"
)
type Pay struct {
}
var ctx = context.Background()
var productService service.ProductService
// 注册路由
func InitPay(group *gin.RouterGroup) {
var p Pay
{
payPublicGroup := group.Group("pay")
payPublicGroup.POST("callback/:platform", p.Callback) // 微信支付支付回调
payPublicGroup.GET("refund_details/:id", p.RefundDetailsHTML) // 内部退款订单详情页面
payPublicGroup.POST("refund/:id", p.Refund) // 内部退款按钮
payPublicGroup.POST("refund_callback/:platform", p.RefundCallback) // 微信退款回调
payPublicGroup.POST("ali_callback", p.AlipayCallback) // 阿里回调
payPublicGroup.POST("complaint_callback/:platform", p.WxPayComplaintCallback)
}
{
payPrivateGroup := group.Group("pay")
payPrivateGroup.Use(JWTAuth())
payPrivateGroup.POST("prepay", p.Prepay) // 创建微信支付订单
payPrivateGroup.GET("order_list", p.GetOrderList) // 获取订单列表
payPrivateGroup.GET("promotion_list", p.GetPromotionList) // 获取订单列表
payPrivateGroup.POST("ali_prepay", p.AliPrepay) // 创建支付宝支付订单
payPrivateGroup.GET("get_query_cache", p.GetQueryCache) // 获取网页的查询缓存
}
}
func (p *Pay) GetOrderList(c *gin.Context) {
// 从查询参数中获取分页参数
pageSizeStr := c.DefaultQuery("page_size", "10")
pageNumStr := c.DefaultQuery("page_num", "1")
userId := utils.GetUserID(c)
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
pageNum, err := strconv.Atoi(pageNumStr)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, err := orderService.GetList(pageSize, pageNum, userId)
if err != nil {
log.Println("get order list error:", err)
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithData(list, c)
}
func (p *Pay) GetPromotionList(c *gin.Context) {
userId := utils.GetUserID(c)
user, err := userService.GetUserByUserid(userId)
if err != nil {
log.Println("get user info error:", err)
response.FailWithMessage("系统开小差啦, 请稍后再试~", c)
return
}
if user.Promotion == "" {
response.FailWithMessage("您不是代理推广人哦", c)
return
}
// 从查询参数中获取分页参数
pageSizeStr := c.DefaultQuery("page_size", "10")
pageNumStr := c.DefaultQuery("page_num", "1")
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
pageNum, err := strconv.Atoi(pageNumStr)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, err := orderService.GetListByPromotion(pageSize, pageNum, user.Promotion)
if err != nil {
log.Println("get order list error:", err)
response.FailWithMessage(err.Error(), c)
return
}
// 获取总单数和总金额
totalCount, totalAmount, err := orderService.GetSummaryByPromotion(user.Promotion)
if err != nil {
log.Println("get order summary error:", err)
response.FailWithMessage(err.Error(), c)
return
}
// 计算总金额的 30%,并保留两位小数
totalAmount30 := math.Round((totalAmount/30.0)*100) / 100
response.OkWithData(gin.H{
"list": list,
"points": 30,
"total_count": totalCount,
"total_amount": totalAmount30, // 总金额的30%
}, c)
}
func (p *Pay) Prepay(c *gin.Context) {
Claims, err := utils.GetClaims(c)
if err != nil {
log.Println("get claims error:", err)
response.FailWithMessage(err.Error(), c)
return
}
var reqBody request.PrepayReq
err = c.ShouldBindJSON(&reqBody)
if err != nil {
response.FailWithMessage("Failed to read request body, need product", c)
return
}
var (
userid = Claims.Userid
openid string
appid string
mchID string
)
log.Printf("Claims, %+v", Claims)
log.Printf("reqBody, %+v", reqBody)
// 获取用户ip
clientIP := c.ClientIP()
switch reqBody.Platform {
case model.PlatformMPWEIXIN:
openid = Claims.AuthIdentifiers.OpenID
appid = config.ConfigData.System.WxAppId
mchID = config.ConfigData.WxPay.MchID
case model.PlatformMPH5:
openid = Claims.AuthIdentifiers.OpenID
appid = config.ConfigData.System.WxH5AppId
mchID = config.ConfigData.WxPay.MchH5ID
case model.PlatformH5:
appid = config.ConfigData.System.WxH5AppId
mchID = config.ConfigData.WxPay.MchH5ID
case model.PlatformTYDATA:
openid = Claims.AuthIdentifiers.OpenID
appid = config.ConfigData.System.WxTyDataAppId
mchID = config.ConfigData.WxPay.MchH5ID
default:
response.FailWithMessage("ProductName Must be wx or mp-h5 or h5", c)
return
}
product, err := productService.GetProduct(reqBody.ProductName)
if err != nil {
log.Println(err)
response.FailWithMessage(err.Error(), c)
return
}
// 查看用户是否内部号
user, err := userService.GetUserByUserid(Claims.Userid)
if err != nil {
log.Println(err)
response.FailWithMessage(err.Error(), c)
return
}
var amount int64
if user.Inside {
amount = int64(1)
} else {
amount = int64(product.SellPrice)
}
var outTradeNo = fmt.Sprintf("wx_%s", utils.GenerateOrderNumber())
var resp interface{}
if reqBody.Platform == model.PlatformMPWEIXIN {
resp, err = orderService.WechatJSAPIPrepay(appid, mchID, product, outTradeNo, amount, openid, model.PlatformMPWEIXIN, global.GlobalData.PayClient)
if err != nil {
log.Printf("【创建微信支付订单】创建支付失败,系统错误: %v", err)
response.FailWithMessage("创建支付失败,系统错误", c)
return
}
} else if reqBody.Platform == model.PlatformMPH5 {
resp, err = orderService.WechatJSAPIPrepay(appid, mchID, product, outTradeNo, amount, openid, model.PlatformMPH5, global.GlobalData.PayH5Client)
if err != nil {
log.Printf("【创建微信支付订单】创建支付失败,系统错误: %v", err)
response.FailWithMessage("创建支付失败,系统错误", c)
return
}
} else if reqBody.Platform == model.PlatformTYDATA {
resp, err = orderService.WechatJSAPIPrepay(appid, mchID, product, outTradeNo, amount, openid, model.PlatformTYDATA, global.GlobalData.PayH5Client)
if err != nil {
log.Printf("【创建微信支付订单】创建支付失败,系统错误: %v", err)
response.FailWithMessage("创建支付失败,系统错误", c)
return
}
} else {
resp, err = orderService.WechatH5Prepay(appid, mchID, product, outTradeNo, amount, clientIP, global.GlobalData.PayH5Client)
if err != nil {
log.Printf("【创建微信支付订单】创建支付失败,系统错误: %v", err)
response.FailWithMessage("创建支付失败,系统错误", c)
return
}
}
var payOrder = model.PayOrder{
OutTradeNo: outTradeNo,
Amount: amount,
Userid: userid,
PayStatus: model.PayStatusNotPay,
ProductID: product.ID,
Product: &product,
Platform: reqBody.Platform,
PaymentMethod: model.PaymentMethod_WECHAT,
Promotion: reqBody.Promotion,
}
err = db.DB.Create(&payOrder).Error
if err != nil {
log.Printf("create payOrder error%v", err)
}
response.OkWithData(resp, c)
}
func (p *Pay) Callback(c *gin.Context) {
ctx := c //这个参数是context.Background()
cRequest := c.Request //这个值是*http.Request
platform := c.Param("platform")
var mchID string
var mchCertificateSerialNumber string
var mchAPIv3Key string
var privateKeyPath string
switch platform {
case model.PlatformMPWEIXIN:
mchID = config.ConfigData.WxPay.MchID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchCertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchAPIv3Key
privateKeyPath = "merchant/mp/apiclient_key.pem"
case model.PlatformH5, model.PlatformMPH5, model.PlatformTYDATA:
mchID = config.ConfigData.WxPay.MchH5ID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchH5CertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchH5APIv3Key
privateKeyPath = "merchant/mph5/apiclient_key.pem"
}
mchPrivateKey, err := wxutils.LoadPrivateKeyWithPath(privateKeyPath)
if err != nil {
log.Printf("load merchant private key error")
return
}
// 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器
err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(ctx, mchPrivateKey, mchCertificateSerialNumber, mchID, mchAPIv3Key)
if err != nil {
log.Printf("register downloader with private key error")
return
}
// 2. 获取商户号对应的微信支付平台证书访问器
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
// 3. 使用证书访问器初始化 `notify.Handler`
handler := notify.NewNotifyHandler(mchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))
transaction := new(payments.Transaction)
notifyReq, err := handler.ParseNotifyRequest(context.Background(), cRequest, transaction)
// 如果验签未通过,或者解密失败
if err != nil {
log.Printf("parse notify request error")
return
}
log.Printf("微信支付回调响应: %+v", notifyReq)
var status string
if notifyReq.Summary == "支付成功" {
status = model.PayStatusSuccess
} else {
status = model.PayStatusPayError
}
err = db.DB.Model(&model.PayOrder{}).Where("out_trade_no = ?", transaction.OutTradeNo).Updates(model.PayOrder{PayStatus: status, TransactionId: *transaction.TransactionId}).Error
if err != nil {
log.Printf("订单回调处理错误%v", err)
}
c.String(http.StatusOK, "success")
}
func (p *Pay) RefundDetailsHTML(c *gin.Context) {
encryptedOrderID := c.Param("id")
// 解密订单ID
decryptedOrderID, err := utils.IDDecrypt(encryptedOrderID, "njbh287yfbuyh18suygbhd98")
if err != nil {
log.Printf("解密订单ID(%s)失败:%v", decryptedOrderID, err)
c.String(http.StatusNotFound, "")
return
}
// 将字符串转换为 uint64
u64, err := strconv.ParseUint(decryptedOrderID, 10, 64)
if err != nil {
log.Printf("uint64转换失败: %v", err)
c.String(http.StatusNotFound, "")
return
}
// 将 uint64 转换为 uint
orderID := uint(u64)
order, err := orderService.GetOrderByid(orderID)
if err != nil {
log.Printf("订单(%d)获取失败:%v", orderID, err)
c.String(http.StatusNotFound, "")
return
}
type RenderOrder struct {
ID string
OutTradeNo string
TransactionId string
Userid uint
CreatedAt string
Product string
Amount float64
PayStatus string
}
renderOrder := RenderOrder{
ID: encryptedOrderID,
OutTradeNo: order.OutTradeNo,
TransactionId: order.TransactionId,
Userid: order.Userid,
CreatedAt: order.CreatedAt.Format("2006-01-02 15:04:05"),
Product: order.Product.ProductName,
Amount: float64(order.Amount) / 100,
PayStatus: order.PayStatus,
}
c.HTML(http.StatusOK, "refund.html", renderOrder)
}
func (p *Pay) Refund(c *gin.Context) {
encryptedOrderID := c.Param("id")
// 解密订单ID
decryptedOrderID, err := utils.IDDecrypt(encryptedOrderID, "njbh287yfbuyh18suygbhd98")
if err != nil {
log.Printf("解密订单ID(%s)失败:%v", decryptedOrderID, err)
c.String(http.StatusNotFound, "")
return
}
// 将字符串转换为 uint64
u64, err := strconv.ParseUint(decryptedOrderID, 10, 64)
if err != nil {
log.Printf("uint64转换失败: %v", err)
c.String(http.StatusNotFound, "")
return
}
// 将 uint64 转换为 uint
orderID := uint(u64)
order, err := orderService.GetOrderByid(orderID)
if err != nil {
log.Printf("订单(%d)获取失败:%v", orderID, err)
c.String(http.StatusNotFound, "")
return
}
var payClient *wxcore.Client
switch order.Platform {
case model.PlatformMPWEIXIN:
payClient = global.GlobalData.PayClient
case model.PlatformH5, model.PlatformMPH5, model.PlatformTYDATA:
payClient = global.GlobalData.PayH5Client
}
outRefundNo := utils.GenerateOrderRefundNumber()
svc := refunddomestic.RefundsApiService{Client: payClient}
resp, result, err := svc.Create(ctx,
refunddomestic.CreateRequest{
OutTradeNo: wxcore.String(order.OutTradeNo),
OutRefundNo: wxcore.String(outRefundNo), //退款单号
NotifyUrl: wxcore.String(fmt.Sprintf("%s/%s", config.ConfigData.WxPay.RefundNotifyURL, order.Platform)),
Amount: &refunddomestic.AmountReq{
Currency: wxcore.String("CNY"),
Refund: wxcore.Int64(order.Amount),
Total: wxcore.Int64(order.Amount),
},
},
)
if err != nil {
// 处理错误
log.Printf("微信订单申请退款错误:%s", err)
response.Fail(c)
} else {
// 处理返回结果
log.Printf("微信订单申请退款 response status=%d resp=%s", result.Response.StatusCode, resp)
order.PayStatus = model.PayStatusUnderRefund
err = db.DB.Save(order).Error
if err != nil {
log.Printf("微信订单退款状态修改失败 underRefund Error:%v", err)
}
response.Ok(c)
}
}
func (p *Pay) RefundCallback(c *gin.Context) {
ctx := c //这个参数是context.Background()
cRequest := c.Request //这个值是*http.Request
platform := c.Param("platform")
var mchID string
var mchCertificateSerialNumber string
var mchAPIv3Key string
var privateKeyPath string
switch platform {
case model.PlatformMPWEIXIN:
mchID = config.ConfigData.WxPay.MchID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchCertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchAPIv3Key
privateKeyPath = "merchant/mp/apiclient_key.pem"
case model.PlatformH5, model.PlatformMPH5, model.PlatformTYDATA:
mchID = config.ConfigData.WxPay.MchH5ID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchH5CertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchH5APIv3Key
privateKeyPath = "merchant/mph5/apiclient_key.pem"
}
mchPrivateKey, err := wxutils.LoadPrivateKeyWithPath(privateKeyPath)
if err != nil {
log.Printf("load merchant private key error")
return
}
// 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器
err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(ctx, mchPrivateKey, mchCertificateSerialNumber, mchID, mchAPIv3Key)
if err != nil {
log.Printf("register downloader with private key error")
return
}
// 2. 获取商户号对应的微信支付平台证书访问器
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
// 3. 使用证书访问器初始化 `notify.Handler`
handler := notify.NewNotifyHandler(mchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))
transaction := new(payments.Transaction)
notifyReq, err := handler.ParseNotifyRequest(context.Background(), cRequest, transaction)
// 如果验签未通过,或者解密失败
if err != nil {
log.Printf("parse notify request error")
return
}
log.Printf("微信退款回调响应 notifyReq: %+v", notifyReq)
log.Printf("微信退款回调响应 transaction: %+v", transaction)
order := model.PayOrder{}
err = db.DB.Preload("Product").Where("out_trade_no = ?", transaction.OutTradeNo).First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Printf("【微信退款回调响应】找不到回调相关的订单。OutTradeNo%s", transaction.OutTradeNo)
return
} else {
log.Printf("退款回调处理错误%v", err)
}
}
if notifyReq.EventType == "REFUND.SUCCESS" {
order.PayStatus = model.PayStatusRefund
err = db.DB.Save(&order).Error
if err != nil {
log.Printf("退款回调处理错误%v", err)
} else {
notifyService.SendNotification("退款成功", order.Product.ProductName, order.Userid, order.ID)
}
} else if notifyReq.EventType == "REFUND.ABNORMAL" {
order.PayStatus = model.PayStatusRefundError
err = db.DB.Save(&order).Error
if err != nil {
log.Printf("退款回调处理错误%v", err)
} else {
notifyService.SendNotification("退款异常,请及时处理", order.Product.ProductName, order.Userid, order.ID)
}
}
//log.Printf("微信退款回调响应: %+v", transaction)
c.String(http.StatusOK, "success")
}
// AliPrepay 阿里支付
func (p *Pay) AliPrepay(c *gin.Context) {
var reqBody request.H5PrepayReq
err := c.ShouldBindJSON(&reqBody)
if err != nil {
response.FailWithMessage("Failed to read request body, need product", c)
return
}
Claims, err := utils.GetClaims(c)
if err != nil {
log.Println("get claims error:", err)
response.FailWithMessage(err.Error(), c)
return
}
// 获取商品信息
product, err := productService.GetProduct(reqBody.ProductName)
if err != nil {
log.Println(err)
response.FailWithMessage(err.Error(), c)
return
}
// 生成单号
outTradeNo := fmt.Sprintf("ali_%s", utils.GenerateOrderNumber())
// 查看用户是否内部号
user, err := userService.GetUserByUserid(Claims.Userid)
if err != nil {
log.Println(err)
response.FailWithMessage(err.Error(), c)
return
}
var amount string
var orderAmount int64
log.Printf("is inside%+v,is %t", user, user.Inside)
if user.Inside {
amount = "0.01"
orderAmount = 1
} else {
amount = utils.ConvertCentsToYuan(product.SellPrice)
orderAmount = int64(product.SellPrice)
}
pay := alipay.TradeWapPay{}
pay.NotifyURL = config.ConfigData.AliPay.NotifyURL // 替换为您的回调地址
pay.ReturnURL = fmt.Sprintf("%s%s?callback=true", config.ConfigData.Server.Domain, reqBody.Href) // 支付成功后跳转的地址
pay.Subject = product.ProductName // 订单标题
pay.OutTradeNo = outTradeNo // 生成的唯一订单号
pay.TotalAmount = amount // 支付金额,单位元
pay.ProductCode = "QUICK_WAP_PAY" // WAP支付的产品代码w
payURL, err := global.GlobalData.AliPayClient.TradeWapPay(pay)
if err != nil {
log.Printf("【阿里支付创建】创建订单错误:%v", err)
response.Fail(c)
return
}
ctx := context.Background()
expiration := 5 * time.Minute // 5分钟超时
err = db.RedisClient.Set(ctx, fmt.Sprintf("alipay_%s", outTradeNo), reqBody.QueryData, expiration).Err()
if err != nil {
log.Printf("【阿里支付创建】前端请求数据缓存错误:%v", err)
response.Fail(c)
return
}
var payOrder = model.PayOrder{
OutTradeNo: outTradeNo,
Amount: orderAmount,
Userid: Claims.Userid,
PayStatus: model.PayStatusNotPay,
ProductID: product.ID,
Product: &product,
Platform: reqBody.Platform,
PaymentMethod: model.PaymentMethod_ALIPAY,
}
err = db.DB.Create(&payOrder).Error
if err != nil {
log.Printf("【阿里支付创建】订单保存错误:%v", err)
response.Fail(c)
return
}
response.OkWithData(gin.H{
"PayUrl": payURL.String(),
}, c)
}
// AlipayCallback 阿里支付回调
func (p *Pay) AlipayCallback(c *gin.Context) {
// 解析表单
err := c.Request.ParseForm()
if err != nil {
log.Printf("ali pay callback解析请求表单失败%v", err)
return
}
// DecodeNotification 内部已调用 VerifySign 方法验证签名
noti, err := global.GlobalData.AliPayClient.DecodeNotification(c.Request.Form)
if err != nil {
log.Printf("【阿里支付回调】通知解码失败失败:%v", err)
return
}
log.Printf("【阿里支付回调】接收到支付宝回调,商户订单号:%s", noti.OutTradeNo)
log.Printf("【阿里支付回调】接收到支付宝回调,支付宝订单号:%s", noti.OutTradeNo)
log.Printf("【阿里支付回调】交易状态:%s", noti.TradeStatus)
if noti.TradeStatus == alipay.TradeStatusSuccess {
log.Printf("【阿里支付回调】交易成功")
err = db.DB.Model(&model.PayOrder{}).Where("out_trade_no = ?", noti.OutTradeNo).Updates(model.PayOrder{PayStatus: model.PayStatusSuccess, AliTradeNo: noti.TradeNo}).Error
if err != nil {
log.Printf("【阿里支付回调】订单回调处理错误%v", err)
}
// 确认收到通知消息,不然支付宝后续会继续推送相同的消息
alipay.ACKNotification(c.Writer)
}
}
func (p *Pay) GetQueryCache(c *gin.Context) {
var reqBody request.QueryDataReq
err := c.ShouldBindQuery(&reqBody)
if err != nil {
response.FailWithMessage("Failed to read request body, need out_trade_no", c)
return
}
ctx := context.Background()
key := fmt.Sprintf("alipay_%s", reqBody.OutTradeNo)
result, err := db.RedisClient.Get(ctx, key).Result()
if errors.Is(err, redis.Nil) {
response.FailWithMessage("超时,请重新手动输入数据查询", c)
return
} else if err != nil {
log.Printf("【阿里支付】获取缓存表单数据错误%v", err)
response.FailWithMessage("请重新手动输入数据", c)
return
}
response.OkWithData(result, c)
}
func (p *Pay) WxPayComplaintCallback(c *gin.Context) {
ctx := context.Background()
cRequest := c.Request
platform := c.Param("platform")
var mchID string
var mchCertificateSerialNumber string
var mchAPIv3Key string
var privateKeyPath string
var platformString string
switch platform {
case model.PlatformMPWEIXIN:
platformString = "微信小程序"
mchID = config.ConfigData.WxPay.MchID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchCertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchAPIv3Key
privateKeyPath = "merchant/mp/apiclient_key.pem"
case model.PlatformH5, model.PlatformMPH5, model.PlatformTYDATA:
platformString = "公众号"
mchID = config.ConfigData.WxPay.MchH5ID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchH5CertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchH5APIv3Key
privateKeyPath = "merchant/mph5/apiclient_key.pem"
}
notifyService.SendComplaintNotification(fmt.Sprintf("%s投诉", platformString), "", platformString, "")
mchPrivateKey, err := wxutils.LoadPrivateKeyWithPath(privateKeyPath)
if err != nil {
log.Printf("【%s投诉通知回调】加载商户私钥失败: %v", platformString, err)
return
}
// 注册下载器
err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(ctx, mchPrivateKey, mchCertificateSerialNumber, mchID, mchAPIv3Key)
if err != nil {
log.Printf("【%s投诉通知回调】注册下载器失败: %v", platformString, err)
return
}
// 获取微信支付平台证书访问器
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
// 初始化 notify.Handler
handler := notify.NewNotifyHandler(mchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))
// 解析并验证通知请求
content := make(map[string]interface{})
notifyReq, err := handler.ParseNotifyRequest(ctx, cRequest, &content)
if err != nil {
log.Printf("【%s投诉通知回调】解析通知请求失败: %v", platformString, err)
return
}
// 打印通知摘要和解析后的内容
log.Printf("【%s投诉通知回调】通知信息: %s", platformString, notifyReq.Summary)
log.Printf("【%s投诉通知回调】通知数据: %s", platformString, content)
// 根据解析后的数据处理通知
actionType, ok := content["action_type"].(string)
if !ok {
log.Printf("【%s投诉通知回调】通知内容中缺少action_type字段", platformString)
return
}
complaintID, ok := content["complaint_id"].(string)
if !ok {
log.Printf("【%s投诉通知回调】通知内容中缺少complaint_id字段", platformString)
return
}
switch actionType {
case "CREATE_COMPLAINT":
message := "用户提交了新的投诉"
notifyService.SendComplaintNotification(message, "新投诉", platformString, complaintID)
case "CONTINUE_COMPLAINT":
message := "用户继续投诉"
notifyService.SendComplaintNotification(message, "继续投诉", platformString, complaintID)
case "USER_RESPONSE":
message := "用户在投诉单中添加了新留言"
notifyService.SendComplaintNotification(message, "用户留言", platformString, complaintID)
case "RESPONSE_BY_PLATFORM":
message := "平台在投诉单中添加了新留言"
notifyService.SendComplaintNotification(message, "平台留言", platformString, complaintID)
case "SELLER_REFUND":
message := "商户发起了全额退款"
notifyService.SendComplaintNotification(message, "全额退款", platformString, complaintID)
case "MERCHANT_RESPONSE":
message := "商户对投诉进行了回复"
notifyService.SendComplaintNotification(message, "商户回复", platformString, complaintID)
case "MERCHANT_CONFIRM_COMPLETE":
message := "商户标记投诉处理完成"
notifyService.SendComplaintNotification(message, "投诉处理完成", platformString, complaintID)
case "MERCHANT_APPROVE_REFUND":
message := "商户同意了退款"
notifyService.SendComplaintNotification(message, "同意退款", platformString, complaintID)
case "MERCHANT_REJECT_REFUND":
message := "商户拒绝了退款"
notifyService.SendComplaintNotification(message, "拒绝退款", platformString, complaintID)
case "REFUND_SUCCESS":
message := "退款已成功到账"
notifyService.SendComplaintNotification(message, "退款到账", platformString, complaintID)
default:
message := "未知投诉类型"
notifyService.SendComplaintNotification(message, "未知投诉类型", platformString, complaintID)
log.Printf("【%s投诉通知回调】收到未知类型的投诉通知: %s", platformString, actionType)
}
// 返回成功响应
c.String(http.StatusOK, "success")
}