qnc-server-old/service/order.go

266 lines
9.7 KiB
Go
Raw Permalink Normal View History

2024-09-14 10:48:09 +08:00
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
}
2024-12-25 11:59:33 +08:00
func (a *OrderService) GetListByPromotion(pageSize int, pageNum int, promotion string) (list []model.PayOrder, err error) {
offset := (pageNum - 1) * pageSize
result := db.DB.Preload("Product").Where("promotion = ? AND pay_status IN ?", promotion, []string{model.PayStatusSuccess}).Order("id desc").Limit(pageSize).Offset(offset).Find(&list)
if result.Error != nil {
return list, result.Error
}
return list, nil
}
func (a *OrderService) GetSummaryByPromotion(promotion string) (totalCount int64, totalAmount float64, err error) {
var count int64
var amount float64
// 查询总单数
if err := db.DB.Model(&model.PayOrder{}).
Where("promotion = ? AND pay_status IN ?", promotion, []string{model.PayStatusSuccess}).
Count(&count).Error; err != nil {
return 0, 0, err
}
// 查询总金额,使用 COALESCE 防止 NULL
if err := db.DB.Model(&model.PayOrder{}).
Where("promotion = ? AND pay_status IN ?", promotion, []string{model.PayStatusSuccess}).
Select("COALESCE(SUM(amount), 0)").Scan(&amount).Error; err != nil {
return 0, 0, err
}
return count, amount, nil
}
2024-09-14 10:48:09 +08:00
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
2024-12-25 11:59:33 +08:00
case model.PlatformMPH5, model.PlatformTYDATA:
2024-09-14 10:48:09 +08:00
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)
}
}