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 )
}
}