You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
310 lines
9.8 KiB
310 lines
9.8 KiB
package pay
|
|
|
|
import (
|
|
"base/app/common"
|
|
"base/app/config"
|
|
"base/app/constant"
|
|
"base/app/model"
|
|
"context"
|
|
"errors"
|
|
"git.oa00.com/go/logger"
|
|
"git.oa00.com/go/mysql"
|
|
"git.oa00.com/go/pay"
|
|
"github.com/shopspring/decimal"
|
|
"github.com/smartwalle/alipay/v3"
|
|
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
|
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/app"
|
|
"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"
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
"time"
|
|
)
|
|
|
|
var PayLogic = &payLogic{}
|
|
var MapNotify = map[uint]Notify{}
|
|
|
|
type payLogic struct {
|
|
}
|
|
|
|
// Pay @Title 支付
|
|
func (p *payLogic) Pay(orderId, orderType, payType uint, amount decimal.Decimal, subject string, openId string) (result interface{}, err error) {
|
|
payNo, err := p.name(orderId, orderType, payType, amount)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
if !config.IsProd() {
|
|
// 测试金额
|
|
amount = decimal.NewFromFloat(0.01)
|
|
}
|
|
switch payType {
|
|
case model.PayTypeAli: // 支付宝
|
|
param := alipay.TradeAppPay{}
|
|
param.OutTradeNo = payNo
|
|
param.TotalAmount = amount.String()
|
|
param.Subject = subject
|
|
param.TimeExpire = time.Now().Add(time.Minute * time.Duration(config.Config.Alipay.TimeExpire)).Format("2006-01-02 15:04:05")
|
|
param.NotifyURL = config.Config.Alipay.NotifyURL
|
|
result, err = pay.Alipay.TradeAppPay(param)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
case model.PayTypeWx: // 微信
|
|
apiService := app.AppApiService{Client: pay.Wxpay.Client}
|
|
result, _, err = apiService.PrepayWithRequestPayment(context.Background(), app.PrepayRequest{
|
|
Appid: &config.Config.Wxpay.BrokerAppId,
|
|
Mchid: &config.Config.Wxpay.MchID,
|
|
Description: &subject,
|
|
OutTradeNo: &payNo,
|
|
TimeExpire: core.Time(time.Now().Add(time.Minute * time.Duration(config.Config.Alipay.TimeExpire))),
|
|
NotifyUrl: &config.Config.Wxpay.NotifyURL,
|
|
Amount: &app.Amount{
|
|
Total: core.Int64(amount.Mul(decimal.NewFromInt(100)).IntPart()),
|
|
Currency: core.String("CNY"),
|
|
},
|
|
})
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
case model.PayTypeWxapp:
|
|
apiService := jsapi.JsapiApiService{Client: pay.Wxpay.Client}
|
|
result, _, err = apiService.PrepayWithRequestPayment(context.Background(), jsapi.PrepayRequest{
|
|
Appid: &config.Config.Wxpay.WxappAppId,
|
|
Mchid: &config.Config.Wxpay.MchID,
|
|
Description: &subject,
|
|
OutTradeNo: &payNo,
|
|
TimeExpire: core.Time(time.Now().Add(time.Minute * time.Duration(config.Config.Wxpay.TimeExpire))),
|
|
NotifyUrl: &config.Config.Wxpay.NotifyURL,
|
|
Amount: &jsapi.Amount{
|
|
Total: core.Int64(amount.Mul(decimal.NewFromInt(100)).IntPart()),
|
|
Currency: core.String("CNY"),
|
|
},
|
|
Payer: &jsapi.Payer{
|
|
Openid: &openId,
|
|
},
|
|
})
|
|
case model.PayTypeH5Ali:
|
|
param := alipay.TradeAppPay{}
|
|
param.OutTradeNo = payNo
|
|
param.TotalAmount = amount.String()
|
|
param.Subject = subject
|
|
param.TimeExpire = time.Now().Add(time.Minute * time.Duration(config.Config.Alipay.TimeExpire)).Format("2006-01-02 15:04:05")
|
|
param.NotifyURL = config.Config.Alipay.NotifyURL
|
|
result, err = pay.Alipay.TradeAppPay(param)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
case model.PayTypeH5Wx:
|
|
apiService := h5.H5ApiService{Client: pay.Wxpay.Client}
|
|
result, _, err = apiService.Prepay(context.Background(), h5.PrepayRequest{
|
|
Appid: &config.Config.Wxpay.BrokerAppId,
|
|
Mchid: &config.Config.Wxpay.MchID,
|
|
Description: &subject,
|
|
OutTradeNo: &payNo,
|
|
TimeExpire: core.Time(time.Now().Add(time.Minute * time.Duration(config.Config.Alipay.TimeExpire))),
|
|
NotifyUrl: &config.Config.Wxpay.NotifyURL,
|
|
Amount: &h5.Amount{
|
|
Total: core.Int64(amount.Mul(decimal.NewFromInt(100)).IntPart()),
|
|
Currency: core.String("CNY"),
|
|
},
|
|
SceneInfo: &h5.SceneInfo{
|
|
PayerClientIp: core.String("127.0.0.1"),
|
|
H5Info: &h5.H5Info{
|
|
Type: core.String("Wap"),
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
default:
|
|
return result, errors.New("支付方式错误")
|
|
}
|
|
return
|
|
}
|
|
|
|
// Refund @Title 退款
|
|
func (p *payLogic) Refund(tx *gorm.DB, payId uint) error {
|
|
payModel := model.Pay{Id: payId}
|
|
if tx.First(&payModel).Error != nil {
|
|
return errors.New("退款失败")
|
|
}
|
|
if payModel.Status == model.PayStatusRefund {
|
|
return errors.New("已退款,请勿重复操作")
|
|
}
|
|
if payModel.Status != model.PayStatusPay {
|
|
return errors.New("未支付")
|
|
}
|
|
if tx.Model(&model.Pay{}).Where(&payModel).Updates(map[string]interface{}{
|
|
"status": model.PayStatusRefund,
|
|
"refund_time": time.Now(),
|
|
}).RowsAffected != 1 {
|
|
return errors.New("退款失败")
|
|
}
|
|
if !config.IsProd() {
|
|
// 测试金额
|
|
payModel.Amount = decimal.NewFromFloat(0.01)
|
|
}
|
|
switch payModel.PayType {
|
|
case model.PayTypeAli: // 支付宝
|
|
param := alipay.TradeFastPayRefundQuery{
|
|
OutTradeNo: payModel.PaySn,
|
|
}
|
|
_, err := pay.Alipay.TradeFastPayRefundQuery(param)
|
|
if err != nil {
|
|
logger.Logger.Error("支付宝退款失败", zap.Any("pay", payModel), zap.Error(err))
|
|
return errors.New("退款失败")
|
|
}
|
|
case model.PayTypeWx: // 微信
|
|
amount := payModel.Amount.Mul(decimal.NewFromInt(100)).IntPart()
|
|
apiService := refunddomestic.RefundsApiService{Client: pay.Wxpay.Client}
|
|
result, _, err := apiService.Create(context.Background(), refunddomestic.CreateRequest{
|
|
OutTradeNo: &payModel.PaySn,
|
|
OutRefundNo: &payModel.PaySn,
|
|
NotifyUrl: &config.Config.Wxpay.NotifyURL, // 同步处理退款结果
|
|
Amount: &refunddomestic.AmountReq{
|
|
Currency: core.String("CNY"),
|
|
Refund: &amount,
|
|
Total: &amount,
|
|
},
|
|
})
|
|
if err != nil {
|
|
logger.Logger.Error("微信退款失败", zap.Any("pay", payModel), zap.Error(err))
|
|
return errors.New("退款失败")
|
|
}
|
|
if *result.Status != refunddomestic.STATUS_SUCCESS { // 退款失败
|
|
logger.Logger.Error("微信退款失败", zap.Any("pay", payModel), zap.Error(err))
|
|
return errors.New("退款失败")
|
|
}
|
|
case model.PayTypeWxapp:
|
|
amount := payModel.Amount.Mul(decimal.NewFromInt(100)).IntPart()
|
|
apiService := refunddomestic.RefundsApiService{Client: pay.Wxpay.Client}
|
|
result, _, err := apiService.Create(context.Background(), refunddomestic.CreateRequest{
|
|
OutTradeNo: &payModel.PaySn,
|
|
OutRefundNo: &payModel.PaySn,
|
|
NotifyUrl: &config.Config.Wxpay.NotifyURL, // 同步处理退款结果
|
|
Amount: &refunddomestic.AmountReq{
|
|
Currency: core.String("CNY"),
|
|
Refund: &amount,
|
|
Total: &amount,
|
|
},
|
|
})
|
|
if err != nil {
|
|
logger.Logger.Error("微信小程序退款失败", zap.Any("pay", payModel), zap.Error(err))
|
|
return errors.New("退款失败")
|
|
}
|
|
logger.Logger.Info("退款回参", zap.Any("result", result))
|
|
if *result.Status != refunddomestic.STATUS_SUCCESS && *result.Status != refunddomestic.STATUS_PROCESSING { // 退款失败
|
|
logger.Logger.Error("微信小程序退款失败", zap.Any("pay", payModel), zap.Error(err))
|
|
return errors.New("退款失败")
|
|
}
|
|
default:
|
|
return errors.New("退款失败")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type Notify interface {
|
|
Success(db *gorm.DB, orderId uint, payModel model.Pay) error
|
|
Fail(db *gorm.DB, orderId uint, payModel model.Pay) error
|
|
RefundSuccess(db *gorm.DB, orderId uint, payModel model.Pay) error
|
|
}
|
|
|
|
// Success @Title 付款成功
|
|
func (p *payLogic) Success(paySn, payTime string) error {
|
|
payModle := model.Pay{PaySn: paySn}
|
|
if mysql.Db.Where(&payModle).First(&payModle).Error != nil {
|
|
return errors.New("付款单号错误")
|
|
}
|
|
if payModle.Status == model.PayStatusPay {
|
|
return nil
|
|
}
|
|
return mysql.Db.Transaction(func(tx *gorm.DB) error {
|
|
if tx.Model(&payModle).Where(&payModle).UpdateColumns(map[string]interface{}{
|
|
"status": model.PayStatusPay,
|
|
"pay_time": payTime,
|
|
}).Error != nil {
|
|
return errors.New("付款失败")
|
|
}
|
|
for orderType, notify := range MapNotify {
|
|
if orderType == payModle.OrderType {
|
|
if err := notify.Success(tx, payModle.OrderId, payModle); err != nil {
|
|
return errors.New("付款失败")
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// Fail @Title 付款失败
|
|
func (p *payLogic) Fail(paySn string) error {
|
|
payModle := model.Pay{PaySn: paySn}
|
|
if mysql.Db.Where(&payModle).First(&payModle).Error != nil {
|
|
return errors.New("付款单号错误")
|
|
}
|
|
return mysql.Db.Transaction(func(tx *gorm.DB) error {
|
|
if tx.Where(&payModle).UpdateColumns(map[string]interface{}{
|
|
"status": model.PayStatusTimeout,
|
|
}).Error != nil {
|
|
return errors.New("处理失败")
|
|
}
|
|
for orderType, notify := range MapNotify {
|
|
if orderType == payModle.OrderType {
|
|
if err := notify.Fail(tx, payModle.OrderId, payModle); err != nil {
|
|
return errors.New("付款失败")
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// @Title 创建付款记录
|
|
func (p *payLogic) name(orderId, orderType, payType uint, amount decimal.Decimal) (payNo string, err error) {
|
|
payNo, err = common.GenNo(constant.NoPrefixPaySn)
|
|
if err != nil {
|
|
return
|
|
}
|
|
payModel := model.Pay{
|
|
PaySn: payNo,
|
|
OrderType: orderType,
|
|
OrderId: orderId,
|
|
Amount: amount,
|
|
Status: model.PayStatusUnPay,
|
|
PayType: payType,
|
|
}
|
|
if mysql.Db.Create(&payModel).Error != nil {
|
|
return payNo, errors.New("下单失败")
|
|
}
|
|
return
|
|
}
|
|
|
|
// RefundSuccess @Title 退款成功
|
|
func (p *payLogic) RefundSuccess(paySn, payTime string) error {
|
|
payModel := model.Pay{PaySn: paySn}
|
|
if mysql.Db.Where(&payModel).First(&payModel).Error != nil {
|
|
return errors.New("付款单号错误")
|
|
}
|
|
if payModel.Status == model.PayStatusRefund {
|
|
return nil
|
|
}
|
|
return mysql.Db.Transaction(func(tx *gorm.DB) error {
|
|
if tx.Model(&model.Pay{}).Where(&payModel).Updates(map[string]interface{}{
|
|
"status": model.PayStatusRefund,
|
|
"refund_time": payTime,
|
|
}).Error != nil {
|
|
return errors.New("付款失败")
|
|
}
|
|
for orderType, notify := range MapNotify {
|
|
if orderType == payModel.OrderType {
|
|
if err := notify.RefundSuccess(tx, payModel.OrderId, payModel); err != nil {
|
|
return errors.New("付款失败")
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|