package service import ( "context" "encoding/json" "errors" "fmt" "github.com/smartwalle/alipay/v3" wxcore "github.com/wechatpay-apiv3/wechatpay-go/core" "github.com/wechatpay-apiv3/wechatpay-go/services/payments/h5" "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" "github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic" "log" "net/http" "qnc-server/config" "qnc-server/db" "qnc-server/global" "qnc-server/model/model" "qnc-server/utils" ) type OrderService struct { } func (a *OrderService) QueryConsumedOrder(userid uint, productEn string) (payOrder model.PayOrder, err error) { err = db.DB.Joins("JOIN product ON product.id = pay_order.product_id").Where("pay_order.userid = ? AND pay_order.pay_status = ? AND pay_order.has_consumed = ? AND product.product_en = ?", userid, model.PayStatusSuccess, model.NotConsumed, productEn).First(&payOrder).Error return } // 付款消费 func (a *OrderService) OrderConsumed(payOrder model.PayOrder) { payOrder.HasConsumed = 1 err := db.DB.Save(&payOrder).Error if err != nil { log.Printf("消费订单失败:%v", err) } } // 失效并退款 func (a *OrderService) OrderFailure(payOrder model.PayOrder) { payOrder.HasConsumed = 2 err := db.DB.Save(&payOrder).Error if err != nil { log.Printf("失效订单失败:%v", err) } } func (a *OrderService) GetList(pageSize int, pageNum int, userId uint) (list []model.PayOrder, err error) { offset := (pageNum - 1) * pageSize result := db.DB.Preload("Product").Where("userid = ? AND pay_status IN ?", userId, []string{model.PayStatusSuccess, model.PayStatusRefund, model.PayStatusUnderRefund}).Order("id desc").Limit(pageSize).Offset(offset).Find(&list) if result.Error != nil { return list, result.Error } return list, nil } func (a *OrderService) GetOrderByid(id uint) (order *model.PayOrder, err error) { order = &model.PayOrder{} err = db.DB.Preload("Product").Where("id = ?", id).First(order).Error if err != nil { return } return } // 微信小程序或公众号 func (a *OrderService) WechatJSAPIPrepay(appid string, mchID string, product model.Product, outTradeNo string, amount int64, openid string, platform string, client *wxcore.Client) (resp *jsapi.PrepayWithRequestPaymentResponse, err error) { prepayReq := jsapi.PrepayRequest{ Appid: &appid, Mchid: &mchID, Description: wxcore.String(product.ProductName), OutTradeNo: wxcore.String(outTradeNo), // 唯一的订单号 Attach: wxcore.String(product.Description), NotifyUrl: wxcore.String(fmt.Sprintf("%s/%s", config.ConfigData.WxPay.NotifyURL, platform)), // 回调地址,需替换为实际地址 Amount: &jsapi.Amount{ Total: wxcore.Int64(amount), // 订单金额,单位为分 }, Payer: &jsapi.Payer{ Openid: wxcore.String(openid), // 用户的 OpenID,通过前端传入 }} // 发起预支付请求 svc := jsapi.JsapiApiService{Client: client} resp, _, err = svc.PrepayWithRequestPayment(ctx, prepayReq) if err != nil { return nil, err } return resp, nil } // 浏览器H5 func (a *OrderService) WechatH5Prepay(appid string, mchID string, product model.Product, outTradeNo string, amount int64, userIP string, client *wxcore.Client) (resp *h5.PrepayResponse, err error) { prepayReq := h5.PrepayRequest{ Appid: wxcore.String(appid), Mchid: wxcore.String(mchID), Description: wxcore.String(product.ProductName), OutTradeNo: wxcore.String(outTradeNo), // 唯一的订单号 Attach: wxcore.String(product.Description), NotifyUrl: wxcore.String(config.ConfigData.WxPay.NotifyURL), // 回调地址,需替换为实际地址 Amount: &h5.Amount{ Total: wxcore.Int64(amount), // 订单金额,单位为分 }, SceneInfo: &h5.SceneInfo{ PayerClientIp: wxcore.String(userIP), // 可通过前端传入 H5Info: &h5.H5Info{ Type: wxcore.String("Wap"), //AppName: wxcore.String("全能查"), //AppUrl: wxcore.String("https://你的域名"), }, }, } // 发起预支付请求 svc := h5.H5ApiService{Client: client} resp, _, err = svc.Prepay(context.Background(), prepayReq) if err != nil { return nil, err } return resp, nil } func (a *OrderService) WeChatRefund(order model.PayOrder) (err error) { var client *wxcore.Client switch order.Platform { case model.PlatformMPWEIXIN: client = global.GlobalData.PayClient case model.PlatformMPH5: client = global.GlobalData.PayH5Client } outRefundNo := utils.GenerateOrderRefundNumber() svc := refunddomestic.RefundsApiService{Client: client} 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) return errors.New(fmt.Sprintf("微信订单申请退款错误:%s", err)) } 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) return errors.New(fmt.Sprintf("微信订单退状态修改失败 underRefund Error:%v", err)) } return nil } } // 创建投诉回调URL func (a *OrderService) RegisterComplaintNotificationURL(client *wxcore.Client, platform string) { url := "https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications" // 请求体数据 requestBody := map[string]string{ "url": fmt.Sprintf("%s/%s/pay/complaint_callback/%s", config.ConfigData.Server.Domain, config.ConfigData.Server.Prefix, platform), // 你的回调URL } // 将请求体序列化为 JSON requestBodyJson, err := json.Marshal(requestBody) if err != nil { log.Fatalf("【%s创建投诉回调URL】请求体JSON序列化失败: %v", platform, err) } // 发送POST请求 result, err := client.Post(context.Background(), url, requestBodyJson) if err != nil { // 尝试解析响应内容,获取详细错误信息 var response map[string]interface{} if err := json.NewDecoder(result.Response.Body).Decode(&response); err != nil { log.Fatalf("【%s创建投诉回调URL】解析错误响应失败: %v", platform, err) } // 检查错误代码和信息 if responseCode, ok := response["code"].(string); ok && responseCode == "PARAM_ERROR" { if responseMessage, ok := response["message"].(string); ok && responseMessage == "数据已存在" { log.Printf("【%s创建投诉回调URL】数据已存在,不需要重新创建: %+v\n", platform, response) return } } log.Fatalf("【%s创建投诉回调URL】请求失败: %v", platform, err) } // 处理响应 defer result.Response.Body.Close() // 检查请求状态 if result.Response.StatusCode != http.StatusOK { log.Fatalf("【%s创建投诉回调URL】请求失败,状态码: %v", platform, result.Response.StatusCode) } // 解析响应 var response map[string]interface{} if err := json.NewDecoder(result.Response.Body).Decode(&response); err != nil { log.Fatalf("【%s创建投诉回调URL】解析响应失败: %v", platform, err) } log.Printf("【%s创建投诉回调URL】投诉通知回调URL创建成功: %+v\n", platform, response) return } func (a *OrderService) AliRefund(ctx context.Context, payOrder model.PayOrder) error { // 创建退款请求参数 refund := alipay.TradeRefund{} refund.OutTradeNo = payOrder.OutTradeNo refund.RefundAmount = utils.ConvertCentsToYuan(int(payOrder.Amount)) // 退款金额,单位元 refund.OutRequestNo = fmt.Sprintf("ali_refund_%s", utils.GenerateOrderNumber()) // 生成的唯一退款请求号 // 发起退款请求 refundResp, err := global.GlobalData.AliPayClient.TradeRefund(ctx, refund) if err != nil { log.Printf("【阿里支付退款】退款请求错误:%v", err) return fmt.Errorf("refund request failed") } if refundResp.IsSuccess() { // 更新订单状态 err = db.DB.Model(&model.PayOrder{}).Where("out_trade_no = ?", payOrder.OutTradeNo).Updates(model.PayOrder{ PayStatus: model.PayStatusRefund, }).Error if err != nil { log.Printf("【阿里支付退款】订单更新错误:%v", err) return fmt.Errorf("order update failed") } return nil } else { log.Printf("【阿里支付退款】退款失败:%v", refundResp.SubMsg) return fmt.Errorf("refund failed: %s", refundResp.SubMsg) } }