diff --git a/app/http/controllers/front/payment_controller.go b/app/http/controllers/front/payment_controller.go index 0f550b2..f22a12f 100644 --- a/app/http/controllers/front/payment_controller.go +++ b/app/http/controllers/front/payment_controller.go @@ -1,8 +1,11 @@ package front import ( + "PaymentCenter/app/third/paymentService" "github.com/gin-gonic/gin" + "github.com/go-pay/gopay" "github.com/go-pay/gopay/alipay" + "github.com/go-pay/gopay/wechat/v3" "github.com/go-pay/xlog" "github.com/qit-team/snow-core/log/logger" @@ -10,45 +13,57 @@ import ( ) // WxCallback 微信支付回调 -//func WxCallback(c *gin.Context) { -// notifyReq, err := wechat.V3ParseNotify(c.Request) -// if err != nil { -// xlog.Error(err) -// return -// } -// -// paymentService.WxPayCallBack(notifyReq) -// -// // ====↓↓↓====异步通知应答====↓↓↓==== -// // 退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功,否则会重试。 -// // 注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。 -// -// // 此写法是 gin 框架返回微信的写法 -// c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"}) -//} +func WxCallback(c *gin.Context) { + appId := c.Param("appId") + logger.Info(c, "WxCallback-回调数据APPID", appId) + if appId == "" { + c.String(http.StatusBadRequest, "%s", "fail") + } + + // todo 查询应用下的支付配置 + + var wxConfig paymentService.WxPay + + logger.Info(c, "WxCallback-回调数据", c.Request) + notifyReq, err := wechat.V3ParseNotify(c.Request) + if err != nil { + logger.Error(c, "WxCallback-回调数据验签失败", err.Error()) + return + } + + err = paymentService.WxPayCallBack(notifyReq, wxConfig) + if err != nil { + logger.Error(c, "WxCallback-回调执行失败,失败原因:", err.Error()) + return + } + + // ====↓↓↓====异步通知应答====↓↓↓==== + // 退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功,否则会重试。 + // 注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。 + c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"}) +} // AliCallback 支付宝支付回调 func AliCallback(c *gin.Context) { + appId := c.Param("appId") + logger.Info(c, "AliCallback-回调数据APPID", appId) + if appId == "" { + c.String(http.StatusBadRequest, "%s", "fail") + } + // todo 查询应用下的支付配置 + notifyReq, err := alipay.ParseNotifyToBodyMap(c.Request) // c.Request 是 gin 框架的写法 logger.Info(c, "AliCallback-回调数据", c.Request) if err != nil { xlog.Error(err) return } - appId := notifyReq.Get("app_id") - if appId == "" { - c.String(http.StatusBadRequest, "%s", "fail") + var aliConfig paymentService.AliPay + err = paymentService.ALiCallBack(notifyReq, aliConfig) + if err != nil { + logger.Error(c, "AliCallback-回调执行失败,失败原因:", err.Error()) + return } - // todo 查询支付宝公钥证书信息,进行验证签名 - alipayPublicCert := "" - ok, err := alipay.VerifySignWithCert([]byte(alipayPublicCert), notifyReq) - if !ok { - logger.Error(c, "AliCallback-回调数据,验签失败") - c.String(http.StatusBadRequest, "%s", "fail") - } - - // todo 拼装数据触发下游回调,数据待定 - c.String(http.StatusOK, "%s", "success") } diff --git a/app/http/entities/front/payment.go b/app/http/entities/front/payment.go index a1a57de..9bad7d0 100644 --- a/app/http/entities/front/payment.go +++ b/app/http/entities/front/payment.go @@ -1,20 +1 @@ package front - -type WxRequest struct { - TradeNo string `json:"tradeNo" validate:"required"` // 交易流水号 - VoucherId string `json:"voucherId"` // 制码批次号 - VoucherCode string `json:"voucherCode"` // 串码 - CnclSt string `json:"cnclSt"` // 核销状态,4-已核销 - RedeemResult string `json:"redeemResult"` // 核销结果,00-成功,09- 失败 - MrchntNo string `json:"mrchntNo"` // 商户号 - Sign string `json:"sign"` -} - -type AliRequest struct { - MerchantId int `form:"merchantId" validate:"required"` - OutTradeNo string `form:"outTradeNo" validate:"required"` - Status string `form:"status" validate:"required"` - RechargeAccount string `form:"rechargeAccount"` - CardCode string `form:"cardCode"` - Sign string `form:"sign" validate:"required"` -} diff --git a/app/http/routes/route.go b/app/http/routes/route.go index cee46c4..a3fe510 100644 --- a/app/http/routes/route.go +++ b/app/http/routes/route.go @@ -50,13 +50,13 @@ func RegisterRoute(router *gin.Engine) { // //} - v1 := router.Group("/v1", middlewares.ValidateRequest()) + v1 := router.Group("/v1") { //回调处理 notify := v1.Group("/notify") { - notify.POST("/wx", front.WxCallback) - notify.POST("/ali", front.AliCallback) + notify.POST("/wx/:appId", front.WxCallback) + notify.POST("/ali/:appId", front.AliCallback) } } diff --git a/app/third/paymentService/ali_service.go b/app/third/paymentService/ali_service.go index 95bf674..0e85140 100644 --- a/app/third/paymentService/ali_service.go +++ b/app/third/paymentService/ali_service.go @@ -75,7 +75,8 @@ func ALiH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, e bm.Set("out_trade_no", payOrderRequest.OrderId). Set("total_amount", amount). Set("subject", payOrderRequest.Description). - Set("notify_url", payCommon.ALI_NOTIFY_URL) + //Set("notify_url", payCommon.ALI_NOTIFY_URL_TEST+payOrderRequest.Ali.AppId). + Set("notify_url", payCommon.ALI_NOTIFY_URL_PROD+payOrderRequest.Ali.AppId) aliRsp, err := aliClient.TradeWapPay(c, bm) if err != nil { @@ -87,6 +88,17 @@ func ALiH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, e return aliRsp, nil } +// ALiCallBack 支付宝支付回调 +func ALiCallBack(notifyReq gopay.BodyMap, aliConfig AliPay) error { + ok, err := alipay.VerifySignWithCert(aliConfig.AlipayPublicCert, notifyReq) + if !ok || err != nil { + return err + } + + // todo 拼装数据触发下游回调,数据待定 + return nil +} + // ALiOrderQuery 查询支付宝订单 func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOrderQueryInfo, error) { // 初始化支付宝客户端 @@ -136,9 +148,10 @@ func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOr amountTotal, _ := strconv.Atoi(aliRsp.Response.TotalAmount) payerTotal, _ := strconv.Atoi(aliRsp.Response.BuyerPayAmount) // 构建数据 + outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo) return PayOrderQueryInfo{ AppId: aliConfig.AppId, - OutTradeNo: aliRsp.Response.OutTradeNo, + OutTradeNo: int64(outTradeNo), TransactionId: aliRsp.Response.TradeNo, TradeState: tradeState, TradeStateDesc: tradeStateDesc, @@ -147,3 +160,110 @@ func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOr PayerTotal: int64(payerTotal * 100), }, nil } + +// AliRefundOrder 支付宝退款申请 +func AliRefundOrder(ctx context.Context, orderRefundRequest OrderRefundRequest) (OrderRefundInfo, error) { + // 初始化支付宝客户端 + AliInitClient(orderRefundRequest.Ali) + + // 获取支付宝客户端 + aliClient, err := GetAliClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return OrderRefundInfo{}, err + } + // 请求参数 + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", orderRefundRequest.OrderId). + Set("refund_amount", orderRefundRequest.RefundAmount). + Set("refund_reason", orderRefundRequest.RefundReason). + Set("out_request_no", orderRefundRequest.RefundOrderId) + + // 发起退款请求 + aliRsp, err := aliClient.TradeRefund(ctx, bm) + if err != nil { + if bizErr, ok := alipay.IsBizError(err); ok { + return OrderRefundInfo{}, bizErr + } + return OrderRefundInfo{}, err + } + + refundFee, _ := strconv.Atoi(aliRsp.Response.RefundFee) + outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo) + return OrderRefundInfo{ + OutTradeNo: int64(outTradeNo), + TransactionId: aliRsp.Response.TradeNo, + RefundFee: int64(refundFee * 100), + RefundOrderId: orderRefundRequest.RefundOrderId, + RefundStatus: payCommon.PAY_REFUND_STATU_SUCCESS, + }, nil +} + +// AliRefundOrderQuery 支付宝订单退款查询 +func AliRefundOrderQuery(ctx context.Context, orderRefundQueryRequest OrderRefundQueryRequest) (OrderRefundInfo, error) { + // 初始化支付宝客户端 + AliInitClient(orderRefundQueryRequest.Ali) + + // 获取支付宝客户端 + aliClient, err := GetAliClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return OrderRefundInfo{}, err + } + + // 请求参数 + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", orderRefundQueryRequest.OrderId). + Set("out_request_no", orderRefundQueryRequest.RefundOrderId). + Set("query_options", []string{"gmt_refund_pay"}) + + // 发起退款查询请求 + aliRsp, err := aliClient.TradeFastPayRefundQuery(ctx, bm) + if err != nil { + if bizErr, ok := alipay.IsBizError(err); ok { + return OrderRefundInfo{}, bizErr + } + return OrderRefundInfo{}, err + } + refundFee, _ := strconv.Atoi(aliRsp.Response.RefundAmount) + outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo) + refundOrderId, _ := strconv.Atoi(aliRsp.Response.OutRequestNo) + return OrderRefundInfo{ + OutTradeNo: int64(outTradeNo), + TransactionId: aliRsp.Response.TradeNo, + RefundFee: int64(refundFee * 100), + RefundOrderId: int64(refundOrderId), + RefundStatus: payCommon.PAY_REFUND_STATU_SUCCESS, + RefundSuccessTime: aliRsp.Response.GmtRefundPay, + }, nil +} + +// AliCloseOrder 支付宝订单关闭 +func AliCloseOrder(ctx context.Context, orderCloseRequest OrderCloseRequest) (OrderCloseInfo, error) { + // 初始化支付宝客户端 + AliInitClient(orderCloseRequest.Ali) + + // 获取支付宝客户端 + aliClient, err := GetAliClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return OrderCloseInfo{}, err + } + // 请求参数 + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", orderCloseRequest.OrderId) + + // 条码支付 + aliRsp, err := aliClient.TradeClose(ctx, bm) + if err != nil { + if bizErr, ok := alipay.IsBizError(err); ok { + return OrderCloseInfo{}, bizErr + } + return OrderCloseInfo{}, err + } + + outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo) + return OrderCloseInfo{ + OutTradeNo: int64(outTradeNo), + }, nil +} diff --git a/app/third/paymentService/payCommon/common.go b/app/third/paymentService/payCommon/common.go index 4fad793..7c376df 100644 --- a/app/third/paymentService/payCommon/common.go +++ b/app/third/paymentService/payCommon/common.go @@ -1,6 +1,9 @@ package payCommon const ( + TEST_URL = "" + PROD_URL = "" + // 支付渠道枚举,1微信JSAPI,2微信H5,3微信app,4微信Native,5微信小程序,6支付宝网页&移动应用,7支付宝小程序,8支付宝JSAPI PAY_CHANNEL_UNKNOWN = 0 PAY_CHANNEL_WECHAT_JSAPI = 1 @@ -17,12 +20,19 @@ const ( PAY_NOT_FOUND_CODE = 404 // 未找到支付渠道 WX_SUCCESS_CODE = 200 // 微信状态码返回成功 - WX_NOTIFY_URL = "" // 微信支付回调地址 - ALI_NOTIFY_URL = "" // 支付宝支付回调地址 + WX_NOTIFY_URL_TEST = TEST_URL + "/v1/notify/wx/appId" // 微信支付回调地址 + WX_NOTIFY_URL_PROD = PROD_URL + "/v1/notify/wx/appId" // 微信支付回调地址 + ALI_NOTIFY_URL_TEST = TEST_URL + "/v1/notify/ali/appId" // 支付宝支付回调地址 + ALI_NOTIFY_URL_PROD = PROD_URL + "/v1/notify/ali/appId" // 支付宝支付回调地址 ORDER_NO_TYPE_ORDER_NO = 2 PAY_CHANNLE_TYPE_WECHAT = 1 // 支付类型: 微信 PAY_CHANNLE_TYPE_ZFB = 2 // 支付类型:支付宝 + + PAY_REFUND_STATU_COMMON = 0 // 未申请 + PAY_REFUND_STATU_ING = 1 // 退款中 + PAY_REFUND_STATU_SUCCESS = 2 // 退款成功 + PAY_REFUND_STATU_FAIL = 3 // 退款失败 ) diff --git a/app/third/paymentService/payment_service.go b/app/third/paymentService/payment_service.go index b2b38b3..1c06d7a 100644 --- a/app/third/paymentService/payment_service.go +++ b/app/third/paymentService/payment_service.go @@ -40,41 +40,32 @@ type PayOrderResponse struct { // PaymentService 统一发起支付 func PaymentService(c context.Context, payOrderRequest PayOrderRequest) (payOrderResponse PayOrderResponse) { + var err error + var info string switch payOrderRequest.ChannelType { case payCommon.PAY_CHANNEL_WECHAT_H5: // 微信H5支付 - info, err := WxH5PayInfo(c, payOrderRequest) - if err != nil { - return PayOrderResponse{ - Code: payCommon.PAY_ERROR_CODE, - ErrorMsg: err.Error(), - } - } - return PayOrderResponse{ - Code: payCommon.PAY_SUCCESS_CODE, - ErrorMsg: "", - Result: info, - } + info, err = WxH5PayInfo(c, payOrderRequest) case payCommon.PAY_CHANNEL_ALIPAY_WEB: // 支付宝H5支付 - info, err := ALiH5PayInfo(c, payOrderRequest) - if err != nil { - return PayOrderResponse{ - Code: payCommon.PAY_ERROR_CODE, - ErrorMsg: err.Error(), - } - } - return PayOrderResponse{ - Code: payCommon.PAY_SUCCESS_CODE, - ErrorMsg: "", - Result: info, - } + info, err = ALiH5PayInfo(c, payOrderRequest) default: return PayOrderResponse{ Code: payCommon.PAY_NOT_FOUND_CODE, ErrorMsg: "暂不支持该支付渠道,请后续再使用", } } + if err != nil { + return PayOrderResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: err.Error(), + } + } + return PayOrderResponse{ + Code: payCommon.PAY_SUCCESS_CODE, + ErrorMsg: "", + Result: info, + } } type PayOrderQueryRequest struct { @@ -92,7 +83,7 @@ type PayOrderQueryResponse struct { type PayOrderQueryInfo struct { AppId string `json:"app_id"` // 应用ID - OutTradeNo string `json:"out_trade_no"` // 商家订单号 + OutTradeNo int64 `json:"out_trade_no"` // 商家订单号 TransactionId string `json:"transaction_id"` // 第三方订单号 TradeState string `json:"trade_state"` // 交易状态 SUCCESS:成功、REFUND:转入退款、NOTPAY:未支付、CLOSED:已关闭 TradeStateDesc string `json:"trade_state_desc"` // 交易描述 @@ -128,3 +119,142 @@ func PayOrderQuery(c context.Context, payOrderQueryRequest PayOrderQueryRequest) Result: info, } } + +type OrderRefundRequest struct { + OrderId int64 `json:"order_id"` // 订单编号 + RefundOrderId int64 `json:"refund_order_id"` // 退款订单号 + RefundReason string `json:"refund_reason"` // 退款原因 + RefundAmount int64 `json:"refund_amount"` // 退款金额,单位分 + PayChannel int `json:"pay_channel"` // 支付渠道类型 1:wx,2:zfb + Wx WxPay `json:"wx"` + Ali AliPay `json:"ali"` +} + +type OrderRefundResponse struct { + Code int `json:"code"` + ErrorMsg string `json:"error_msg"` + Result OrderRefundInfo `json:"result"` +} + +type OrderRefundInfo struct { + OutTradeNo int64 `json:"out_trade_no"` // 商家订单号 + TransactionId string `json:"transaction_id"` // 第三方订单号 + RefundFee int64 `json:"refund_fee"` // 退款金额 + RefundOrderId int64 `json:"refund_order_id"` // 退款订单号 + RefundStatus int `json:"refund_status"` // 退款状态 0:未申请,1:退款中,2:退款成功,3:退款失败 + RefundSuccessTime string `json:"refund_success_time"` // 退款时间 +} + +// OrderRefund 订单退款 +func OrderRefund(c context.Context, orderRefundRequest OrderRefundRequest) OrderRefundResponse { + var err error + var info OrderRefundInfo + switch orderRefundRequest.PayChannel { + case payCommon.PAY_CHANNLE_TYPE_WECHAT: + // 微信H5支付 + info, err = WxOrderRefund(c, orderRefundRequest) + case payCommon.PAY_CHANNLE_TYPE_ZFB: + info, err = AliRefundOrder(c, orderRefundRequest) + default: + return OrderRefundResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: "暂不支持该支付渠道,请后续再使用", + } + } + if err != nil { + return OrderRefundResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: err.Error(), + } + } + return OrderRefundResponse{ + Code: payCommon.PAY_SUCCESS_CODE, + Result: info, + } +} + +type OrderRefundQueryRequest struct { + RefundOrderId int64 `json:"refund_order_id"` // 退款订单号 + OrderId int64 `json:"order_id"` // 支付订单号 + PayChannel int `json:"pay_channel"` // 支付渠道类型 1:wx,2:zfb + Wx WxPay `json:"wx"` + Ali AliPay `json:"ali"` +} +type OrderRefundQueryResponse struct { + Code int `json:"code"` + ErrorMsg string `json:"error_msg"` + Result OrderRefundInfo `json:"result"` +} + +// OrderRefundQuery 订单退款查询 +func OrderRefundQuery(c context.Context, orderRefundQueryRequest OrderRefundQueryRequest) OrderRefundQueryResponse { + var err error + var info OrderRefundInfo + switch orderRefundQueryRequest.PayChannel { + case payCommon.PAY_CHANNLE_TYPE_WECHAT: + // 微信H5支付 + info, err = WxOrderRefundQuery(c, orderRefundQueryRequest) + case payCommon.PAY_CHANNLE_TYPE_ZFB: + info, err = AliRefundOrderQuery(c, orderRefundQueryRequest) + default: + return OrderRefundQueryResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: "暂不支持该支付渠道,请后续再使用", + } + } + if err != nil { + return OrderRefundQueryResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: err.Error(), + } + } + return OrderRefundQueryResponse{ + Code: payCommon.PAY_SUCCESS_CODE, + Result: info, + } +} + +type OrderCloseRequest struct { + OrderId int64 `json:"order_id"` // 支付订单号 + PayChannel int `json:"pay_channel"` // 支付渠道类型 1:wx,2:zfb + Wx WxPay `json:"wx"` + Ali AliPay `json:"ali"` +} + +type OrderCloseResponse struct { + Code int `json:"code"` + ErrorMsg string `json:"error_msg"` + Result OrderCloseInfo `json:"result"` +} + +type OrderCloseInfo struct { + OutTradeNo int64 `json:"out_trade_no"` // 商家订单号 +} + +// OrderClose 关闭订单 +func OrderClose(c context.Context, orderCloseRequest OrderCloseRequest) OrderCloseResponse { + var err error + var info OrderCloseInfo + switch orderCloseRequest.PayChannel { + case payCommon.PAY_CHANNLE_TYPE_WECHAT: + // 微信H5支付 + info, err = WxCloseOrder(c, orderCloseRequest) + case payCommon.PAY_CHANNLE_TYPE_ZFB: + info, err = AliCloseOrder(c, orderCloseRequest) + default: + return OrderCloseResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: "暂不支持该支付渠道,请后续再使用", + } + } + if err != nil { + return OrderCloseResponse{ + Code: payCommon.PAY_ERROR_CODE, + ErrorMsg: err.Error(), + } + } + return OrderCloseResponse{ + Code: payCommon.PAY_SUCCESS_CODE, + Result: info, + } +} diff --git a/app/third/paymentService/wechat_service.go b/app/third/paymentService/wechat_service.go index 3ad2c14..e439381 100644 --- a/app/third/paymentService/wechat_service.go +++ b/app/third/paymentService/wechat_service.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/go-pay/gopay" "github.com/go-pay/gopay/wechat/v3" + "strconv" "sync" "time" ) @@ -72,7 +73,8 @@ func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, er Set("description", payOrderRequest.Description). Set("out_trade_no", payOrderRequest.OrderId). Set("time_expire", expire). - Set("notify_url", payCommon.WX_NOTIFY_URL). + //Set("notify_url", payCommon.WX_NOTIFY_URL_TEST+payOrderRequest.Wx.AppId). + Set("notify_url", payCommon.WX_NOTIFY_URL_PROD+payOrderRequest.Wx.AppId). SetBodyMap("amount", func(bm gopay.BodyMap) { bm.Set("total", payOrderRequest.Amount). Set("currency", "CNY") @@ -94,32 +96,48 @@ func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, er return wxRsp.Response.H5Url, nil } -//func WxPayCallBack(notifyReq *wechat.V3NotifyReq) error { -// // 获取附加数据中的信息 -// appId := notifyReq.Resource.AssociatedData -// -// payOrderRequest := PayOrderRequest{} -// // 初始化微信客户端 -// InitClient(payOrderRequest) -// -// // 获取微信客户端 -// wxClient, err := GetClient() -// if err != nil { -// fmt.Println("Failed to get client:", err) -// return err -// } -// -// // 获取微信平台证书 -// certMap := wxClient.WxPublicKeyMap() -// // 验证异步通知的签名 -// err = notifyReq.VerifySignByPKMap(certMap) -// if err != nil { -// return err -// } -// objPtr := "" -// // 通用通知解密(推荐此方法) -// result := notifyReq.DecryptCipherTextToStruct(apiV3Key, objPtr) -//} +// WxPayCallBack 微信支付回调 +func WxPayCallBack(notifyReq *wechat.V3NotifyReq, wxConfig WxPay) error { + // 初始化微信客户端 + InitClient(wxConfig) + + // 获取微信客户端 + wxClient, err := GetClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return err + } + + // 获取微信平台证书 + certMap := wxClient.WxPublicKeyMap() + // 验证异步通知的签名 + err = notifyReq.VerifySignByPKMap(certMap) + if err != nil { + return err + } + var CallBackInfo struct { + AppId string `json:"app_id"` // 应用ID + Mchid string `json:"mchid"` // 商户号 + OutTradeNo string `json:"out_trade_no"` // 商户系统内部订单号 + TransactionId string `json:"transaction_id"` // 微信系统订单号 + TradeType string `json:"trade_type"` // JSAPI:公众号支付 NATIVE:扫码支付 App:App支付 MICROPAY:付款码支付 MWEB:H5支付 FACEPAY:刷脸支付 + TradeState string `json:"trade_state"` // SUCCESS:支付成功 REFUND:转入退款 NOTPAY:未支付 CLOSED:已关闭 REVOKED:已撤销(付款码支付) USERPAYING:用户支付中(付款码支付) PAYERROR:支付失败(其他原因,如银行返回失败) + TradeStateDesc string `json:"trade_state_desc"` // 交易状态描述 + SuccessTime string `json:"success_time"` // 支付完成时间 + Amount struct { + Total int64 `json:"total"` + PayerTotal int64 `json:"payer_total"` + } `json:"amount"` + } + // 通用通知解密(推荐此方法) + err = notifyReq.DecryptCipherTextToStruct(wxConfig.ApiV3Key, &CallBackInfo) + if err != nil { + return err + } + // todo 返回触发下游回调的格式 + + return nil +} // WxOrderQuery 查询微信订单 func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrderQueryInfo, error) { @@ -156,9 +174,10 @@ func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrder amountTotal := wxRsp.Response.Amount.Total payerTotal := wxRsp.Response.Amount.PayerTotal + outTradeNo, _ := strconv.Atoi(wxRsp.Response.OutTradeNo) return PayOrderQueryInfo{ AppId: wxRsp.Response.Appid, - OutTradeNo: wxRsp.Response.OutTradeNo, + OutTradeNo: int64(outTradeNo), TransactionId: wxRsp.Response.TransactionId, TradeState: tradeState, TradeStateDesc: wxRsp.Response.TradeStateDesc, @@ -167,3 +186,126 @@ func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrder PayerTotal: int64(payerTotal), }, nil } + +// WxOrderRefund 微信退款申请 +func WxOrderRefund(ctx context.Context, orderRefundRequest OrderRefundRequest) (OrderRefundInfo, error) { + // 初始化微信客户端 + InitClient(orderRefundRequest.Wx) + + // 获取微信客户端 + wxClient, err := GetClient() + if err != nil { + return OrderRefundInfo{}, err + } + // 初始化 BodyMap + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", orderRefundRequest.OrderId). + Set("sign_type", "MD5"). + // 必填 退款订单号(程序员定义的) + Set("out_refund_no", orderRefundRequest.RefundOrderId). + // 选填 退款描述 + Set("reason", orderRefundRequest.RefundReason). + SetBodyMap("amount", func(bm gopay.BodyMap) { + // 退款金额:单位是分 + bm.Set("refund", orderRefundRequest.RefundAmount). //实际退款金额 + Set("total", orderRefundRequest.RefundAmount). // 折扣前总金额(不是实际退款数) + Set("currency", "CNY") + }) + // body:参数Body + refund, err := wxClient.V3Refund(ctx, bm) + if err != nil { + return OrderRefundInfo{}, err + } + + // 将非正常退款异常记录 + if refund.Code != wechat.Success { + // 这里时对非正常退款的一些处理message,我们将code统一使用自定义的,然后把message抛出去 + return OrderRefundInfo{}, errors.New(fmt.Sprintf("申请退款接口失败,错误状态码:%d, 错误信息:%s", refund.Code, refund.Error)) + } + + outTradeNo, _ := strconv.Atoi(refund.Response.OutTradeNo) + outRefundNo, _ := strconv.Atoi(refund.Response.OutRefundNo) + refundStatus := payCommon.PAY_REFUND_STATU_COMMON + switch refund.Response.Status { + case "SUCCESS": + refundStatus = payCommon.PAY_REFUND_STATU_SUCCESS + case "CLOSED": + refundStatus = payCommon.PAY_REFUND_STATU_FAIL + case "PROCESSING": + refundStatus = payCommon.PAY_REFUND_STATU_ING + case "ABNORMAL": + refundStatus = payCommon.PAY_REFUND_STATU_FAIL + } + return OrderRefundInfo{ + OutTradeNo: int64(outTradeNo), + TransactionId: refund.Response.TransactionId, + RefundFee: int64(refund.Response.Amount.PayerRefund), + RefundOrderId: int64(outRefundNo), + RefundStatus: refundStatus, + RefundSuccessTime: refund.Response.SuccessTime, + }, nil +} + +// WxOrderRefundQuery 微信订单退款查询 +func WxOrderRefundQuery(ctx context.Context, orderRefundQueryRequest OrderRefundQueryRequest) (OrderRefundInfo, error) { + // 初始化微信客户端 + InitClient(orderRefundQueryRequest.Wx) + + // 获取微信客户端 + wxClient, err := GetClient() + if err != nil { + fmt.Println("Failed to get client:", err) + return OrderRefundInfo{}, err + } + + wxRsp, err := wxClient.V3RefundQuery(ctx, strconv.FormatInt(orderRefundQueryRequest.RefundOrderId, 10), nil) + if err != nil { + return OrderRefundInfo{}, err + } + if wxRsp.Code != wechat.Success { + return OrderRefundInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error)) + } + outTradeNo, _ := strconv.Atoi(wxRsp.Response.OutTradeNo) + outRefundNo, _ := strconv.Atoi(wxRsp.Response.OutRefundNo) + refundStatus := payCommon.PAY_REFUND_STATU_COMMON + switch wxRsp.Response.Status { + case "SUCCESS": + refundStatus = payCommon.PAY_REFUND_STATU_SUCCESS + case "CLOSED": + refundStatus = payCommon.PAY_REFUND_STATU_FAIL + case "PROCESSING": + refundStatus = payCommon.PAY_REFUND_STATU_ING + case "ABNORMAL": + refundStatus = payCommon.PAY_REFUND_STATU_FAIL + } + return OrderRefundInfo{ + OutTradeNo: int64(outTradeNo), + TransactionId: wxRsp.Response.TransactionId, + RefundFee: int64(wxRsp.Response.Amount.PayerRefund), + RefundOrderId: int64(outRefundNo), + RefundStatus: refundStatus, + RefundSuccessTime: wxRsp.Response.SuccessTime, + }, nil +} + +// WxCloseOrder 微信订单关闭 +func WxCloseOrder(ctx context.Context, orderCloseRequest OrderCloseRequest) (OrderCloseInfo, error) { + // 初始化微信客户端 + InitClient(orderCloseRequest.Wx) + + // 获取微信客户端 + wxClient, err := GetClient() + if err != nil { + return OrderCloseInfo{}, err + } + wxRsp, err := wxClient.V3TransactionCloseOrder(ctx, "FY160932049419637602") + if err != nil { + return OrderCloseInfo{}, err + } + if wxRsp.Code != wechat.Success { + return OrderCloseInfo{}, errors.New(fmt.Sprintf("查询订单接口错误,错误状态码:%d, 错误信息:%s", wxRsp.Code, wxRsp.Error)) + } + return OrderCloseInfo{ + OutTradeNo: orderCloseRequest.OrderId, + }, nil +} diff --git a/app/utils/mq/mqmanager.go b/app/utils/mq/mqmanager.go index 8ac56b2..fc92de7 100644 --- a/app/utils/mq/mqmanager.go +++ b/app/utils/mq/mqmanager.go @@ -1,8 +1,6 @@ package mq import ( - common3 "PaymentCenter/app/constants/common" - mq "PaymentCenter/app/utils/mq/mqs" "sync" ) @@ -25,8 +23,8 @@ func (this *CMqManager) InitMq() { this.mqs = make(map[string]Imq) //this.mqs[common.MQ_RABBIT] = RabbitMq{} //this.mqs[common.MQ_NSQ] = NsqMq{} - this.mqs[common3.MQ_NATS] = mq.NatsMq{} - this.mqs[common3.MQ_KFK] = mq.KafkaMq{} + //this.mqs[common3.MQ_NATS] = mq.NatsMq{} + //this.mqs[common3.MQ_KFK] = mq.KafkaMq{} } func (this *CMqManager) GetMqByName(name string) Imq { once.Do(func() {