支付退款+退款单查询+订单关闭

This commit is contained in:
陈俊宏 2024-08-02 16:37:07 +08:00
parent 4d4338f178
commit f1ee9b4196
8 changed files with 508 additions and 112 deletions

View File

@ -1,8 +1,11 @@
package front package front
import ( import (
"PaymentCenter/app/third/paymentService"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/alipay" "github.com/go-pay/gopay/alipay"
"github.com/go-pay/gopay/wechat/v3"
"github.com/go-pay/xlog" "github.com/go-pay/xlog"
"github.com/qit-team/snow-core/log/logger" "github.com/qit-team/snow-core/log/logger"
@ -10,45 +13,57 @@ import (
) )
// WxCallback 微信支付回调 // WxCallback 微信支付回调
//func WxCallback(c *gin.Context) { func WxCallback(c *gin.Context) {
// notifyReq, err := wechat.V3ParseNotify(c.Request) appId := c.Param("appId")
// if err != nil { logger.Info(c, "WxCallback-回调数据APPID", appId)
// xlog.Error(err) if appId == "" {
// return c.String(http.StatusBadRequest, "%s", "fail")
// } }
//
// paymentService.WxPayCallBack(notifyReq) // todo 查询应用下的支付配置
//
// // ====↓↓↓====异步通知应答====↓↓↓==== var wxConfig paymentService.WxPay
// // 退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功否则会重试。
// // 注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。 logger.Info(c, "WxCallback-回调数据", c.Request)
// notifyReq, err := wechat.V3ParseNotify(c.Request)
// // 此写法是 gin 框架返回微信的写法 if err != nil {
// c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"}) 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 支付宝支付回调 // AliCallback 支付宝支付回调
func AliCallback(c *gin.Context) { 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 框架的写法 notifyReq, err := alipay.ParseNotifyToBodyMap(c.Request) // c.Request 是 gin 框架的写法
logger.Info(c, "AliCallback-回调数据", c.Request) logger.Info(c, "AliCallback-回调数据", c.Request)
if err != nil { if err != nil {
xlog.Error(err) xlog.Error(err)
return return
} }
appId := notifyReq.Get("app_id") var aliConfig paymentService.AliPay
if appId == "" { err = paymentService.ALiCallBack(notifyReq, aliConfig)
c.String(http.StatusBadRequest, "%s", "fail") 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") c.String(http.StatusOK, "%s", "success")
} }

View File

@ -1,20 +1 @@
package front 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"`
}

View File

@ -50,13 +50,13 @@ func RegisterRoute(router *gin.Engine) {
// //
//} //}
v1 := router.Group("/v1", middlewares.ValidateRequest()) v1 := router.Group("/v1")
{ {
//回调处理 //回调处理
notify := v1.Group("/notify") notify := v1.Group("/notify")
{ {
notify.POST("/wx", front.WxCallback) notify.POST("/wx/:appId", front.WxCallback)
notify.POST("/ali", front.AliCallback) notify.POST("/ali/:appId", front.AliCallback)
} }
} }

View File

@ -75,7 +75,8 @@ func ALiH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, e
bm.Set("out_trade_no", payOrderRequest.OrderId). bm.Set("out_trade_no", payOrderRequest.OrderId).
Set("total_amount", amount). Set("total_amount", amount).
Set("subject", payOrderRequest.Description). 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) aliRsp, err := aliClient.TradeWapPay(c, bm)
if err != nil { if err != nil {
@ -87,6 +88,17 @@ func ALiH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, e
return aliRsp, nil 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 查询支付宝订单 // ALiOrderQuery 查询支付宝订单
func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOrderQueryInfo, error) { 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) amountTotal, _ := strconv.Atoi(aliRsp.Response.TotalAmount)
payerTotal, _ := strconv.Atoi(aliRsp.Response.BuyerPayAmount) payerTotal, _ := strconv.Atoi(aliRsp.Response.BuyerPayAmount)
// 构建数据 // 构建数据
outTradeNo, _ := strconv.Atoi(aliRsp.Response.OutTradeNo)
return PayOrderQueryInfo{ return PayOrderQueryInfo{
AppId: aliConfig.AppId, AppId: aliConfig.AppId,
OutTradeNo: aliRsp.Response.OutTradeNo, OutTradeNo: int64(outTradeNo),
TransactionId: aliRsp.Response.TradeNo, TransactionId: aliRsp.Response.TradeNo,
TradeState: tradeState, TradeState: tradeState,
TradeStateDesc: tradeStateDesc, TradeStateDesc: tradeStateDesc,
@ -147,3 +160,110 @@ func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOr
PayerTotal: int64(payerTotal * 100), PayerTotal: int64(payerTotal * 100),
}, nil }, 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
}

View File

@ -1,6 +1,9 @@
package payCommon package payCommon
const ( const (
TEST_URL = ""
PROD_URL = ""
// 支付渠道枚举,1微信JSAPI2微信H53微信app4微信Native5微信小程序6支付宝网页&移动应用7支付宝小程序8支付宝JSAPI // 支付渠道枚举,1微信JSAPI2微信H53微信app4微信Native5微信小程序6支付宝网页&移动应用7支付宝小程序8支付宝JSAPI
PAY_CHANNEL_UNKNOWN = 0 PAY_CHANNEL_UNKNOWN = 0
PAY_CHANNEL_WECHAT_JSAPI = 1 PAY_CHANNEL_WECHAT_JSAPI = 1
@ -17,12 +20,19 @@ const (
PAY_NOT_FOUND_CODE = 404 // 未找到支付渠道 PAY_NOT_FOUND_CODE = 404 // 未找到支付渠道
WX_SUCCESS_CODE = 200 // 微信状态码返回成功 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 ORDER_NO_TYPE_ORDER_NO = 2
PAY_CHANNLE_TYPE_WECHAT = 1 // 支付类型: 微信 PAY_CHANNLE_TYPE_WECHAT = 1 // 支付类型: 微信
PAY_CHANNLE_TYPE_ZFB = 2 // 支付类型:支付宝 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 // 退款失败
) )

View File

@ -40,41 +40,32 @@ type PayOrderResponse struct {
// PaymentService 统一发起支付 // PaymentService 统一发起支付
func PaymentService(c context.Context, payOrderRequest PayOrderRequest) (payOrderResponse PayOrderResponse) { func PaymentService(c context.Context, payOrderRequest PayOrderRequest) (payOrderResponse PayOrderResponse) {
var err error
var info string
switch payOrderRequest.ChannelType { switch payOrderRequest.ChannelType {
case payCommon.PAY_CHANNEL_WECHAT_H5: case payCommon.PAY_CHANNEL_WECHAT_H5:
// 微信H5支付 // 微信H5支付
info, err := WxH5PayInfo(c, payOrderRequest) 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,
}
case payCommon.PAY_CHANNEL_ALIPAY_WEB: case payCommon.PAY_CHANNEL_ALIPAY_WEB:
// 支付宝H5支付 // 支付宝H5支付
info, err := ALiH5PayInfo(c, payOrderRequest) 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,
}
default: default:
return PayOrderResponse{ return PayOrderResponse{
Code: payCommon.PAY_NOT_FOUND_CODE, Code: payCommon.PAY_NOT_FOUND_CODE,
ErrorMsg: "暂不支持该支付渠道,请后续再使用", 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 { type PayOrderQueryRequest struct {
@ -92,7 +83,7 @@ type PayOrderQueryResponse struct {
type PayOrderQueryInfo struct { type PayOrderQueryInfo struct {
AppId string `json:"app_id"` // 应用ID AppId string `json:"app_id"` // 应用ID
OutTradeNo string `json:"out_trade_no"` // 商家订单号 OutTradeNo int64 `json:"out_trade_no"` // 商家订单号
TransactionId string `json:"transaction_id"` // 第三方订单号 TransactionId string `json:"transaction_id"` // 第三方订单号
TradeState string `json:"trade_state"` // 交易状态 SUCCESS成功、REFUND转入退款、NOTPAY未支付、CLOSED已关闭 TradeState string `json:"trade_state"` // 交易状态 SUCCESS成功、REFUND转入退款、NOTPAY未支付、CLOSED已关闭
TradeStateDesc string `json:"trade_state_desc"` // 交易描述 TradeStateDesc string `json:"trade_state_desc"` // 交易描述
@ -128,3 +119,142 @@ func PayOrderQuery(c context.Context, payOrderQueryRequest PayOrderQueryRequest)
Result: info, 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,
}
}

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"github.com/go-pay/gopay" "github.com/go-pay/gopay"
"github.com/go-pay/gopay/wechat/v3" "github.com/go-pay/gopay/wechat/v3"
"strconv"
"sync" "sync"
"time" "time"
) )
@ -72,7 +73,8 @@ func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, er
Set("description", payOrderRequest.Description). Set("description", payOrderRequest.Description).
Set("out_trade_no", payOrderRequest.OrderId). Set("out_trade_no", payOrderRequest.OrderId).
Set("time_expire", expire). 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) { SetBodyMap("amount", func(bm gopay.BodyMap) {
bm.Set("total", payOrderRequest.Amount). bm.Set("total", payOrderRequest.Amount).
Set("currency", "CNY") Set("currency", "CNY")
@ -94,32 +96,48 @@ func WxH5PayInfo(c context.Context, payOrderRequest PayOrderRequest) (string, er
return wxRsp.Response.H5Url, nil return wxRsp.Response.H5Url, nil
} }
//func WxPayCallBack(notifyReq *wechat.V3NotifyReq) error { // WxPayCallBack 微信支付回调
// // 获取附加数据中的信息 func WxPayCallBack(notifyReq *wechat.V3NotifyReq, wxConfig WxPay) error {
// appId := notifyReq.Resource.AssociatedData // 初始化微信客户端
// InitClient(wxConfig)
// payOrderRequest := PayOrderRequest{}
// // 初始化微信客户端 // 获取微信客户端
// InitClient(payOrderRequest) wxClient, err := GetClient()
// if err != nil {
// // 获取微信客户端 fmt.Println("Failed to get client:", err)
// wxClient, err := GetClient() return err
// if err != nil { }
// fmt.Println("Failed to get client:", err)
// return err // 获取微信平台证书
// } certMap := wxClient.WxPublicKeyMap()
// // 验证异步通知的签名
// // 获取微信平台证书 err = notifyReq.VerifySignByPKMap(certMap)
// certMap := wxClient.WxPublicKeyMap() if err != nil {
// // 验证异步通知的签名 return err
// err = notifyReq.VerifySignByPKMap(certMap) }
// if err != nil { var CallBackInfo struct {
// return err AppId string `json:"app_id"` // 应用ID
// } Mchid string `json:"mchid"` // 商户号
// objPtr := "" OutTradeNo string `json:"out_trade_no"` // 商户系统内部订单号
// // 通用通知解密(推荐此方法) TransactionId string `json:"transaction_id"` // 微信系统订单号
// result := notifyReq.DecryptCipherTextToStruct(apiV3Key, objPtr) TradeType string `json:"trade_type"` // JSAPI公众号支付 NATIVE扫码支付 AppApp支付 MICROPAY付款码支付 MWEBH5支付 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 查询微信订单 // WxOrderQuery 查询微信订单
func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrderQueryInfo, error) { 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 amountTotal := wxRsp.Response.Amount.Total
payerTotal := wxRsp.Response.Amount.PayerTotal payerTotal := wxRsp.Response.Amount.PayerTotal
outTradeNo, _ := strconv.Atoi(wxRsp.Response.OutTradeNo)
return PayOrderQueryInfo{ return PayOrderQueryInfo{
AppId: wxRsp.Response.Appid, AppId: wxRsp.Response.Appid,
OutTradeNo: wxRsp.Response.OutTradeNo, OutTradeNo: int64(outTradeNo),
TransactionId: wxRsp.Response.TransactionId, TransactionId: wxRsp.Response.TransactionId,
TradeState: tradeState, TradeState: tradeState,
TradeStateDesc: wxRsp.Response.TradeStateDesc, TradeStateDesc: wxRsp.Response.TradeStateDesc,
@ -167,3 +186,126 @@ func WxOrderQuery(ctx context.Context, wxConfig WxPay, orderNo string) (PayOrder
PayerTotal: int64(payerTotal), PayerTotal: int64(payerTotal),
}, nil }, 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
}

View File

@ -1,8 +1,6 @@
package mq package mq
import ( import (
common3 "PaymentCenter/app/constants/common"
mq "PaymentCenter/app/utils/mq/mqs"
"sync" "sync"
) )
@ -25,8 +23,8 @@ func (this *CMqManager) InitMq() {
this.mqs = make(map[string]Imq) this.mqs = make(map[string]Imq)
//this.mqs[common.MQ_RABBIT] = RabbitMq{} //this.mqs[common.MQ_RABBIT] = RabbitMq{}
//this.mqs[common.MQ_NSQ] = NsqMq{} //this.mqs[common.MQ_NSQ] = NsqMq{}
this.mqs[common3.MQ_NATS] = mq.NatsMq{} //this.mqs[common3.MQ_NATS] = mq.NatsMq{}
this.mqs[common3.MQ_KFK] = mq.KafkaMq{} //this.mqs[common3.MQ_KFK] = mq.KafkaMq{}
} }
func (this *CMqManager) GetMqByName(name string) Imq { func (this *CMqManager) GetMqByName(name string) Imq {
once.Do(func() { once.Do(func() {