Files
jx-callback/business/jxstore/financial/financial.go
2022-07-25 10:59:23 +08:00

422 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package financial
import (
"crypto/hmac"
"crypto/md5"
"crypto/sha256"
"encoding/json"
"fmt"
"git.rosy.net.cn/jx-callback/business/q_bida"
"github.com/astaxie/beego/client/orm"
"sort"
"strings"
"time"
"git.rosy.net.cn/baseapi/platformapi/tonglianpayapi"
"git.rosy.net.cn/baseapi/platformapi/wxpayapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
const (
sigKey = "sign"
sigTypeMd5 = "MD5"
sigTypeSha256 = "HMAC-SHA256"
)
var (
payMap = map[string]*wxpayapi.API{
"weixinapp": api.WxpayAPI,
"weixinmini": api.WxpayAPI2,
}
)
func (p *PayHandler) CreatePay(txDB orm.TxOrmer, subAppID string) (err error) {
switch p.PayType {
case model.PayTypeTL:
param := &tonglianpayapi.CreateUnitorderOrderParam{
Trxamt: p.Order.PayPrice,
NotifyUrl: globals.TLPayNotifyURL,
Reqsn: p.Order.OrderID,
PayType: p.VendorPayType,
}
//暂时做兼容处理
if p.VendorPayType == "JSAPI" {
param.PayType = tonglianpayapi.PayTypeWxXcx
}
if p.VendorPayType == tonglianpayapi.PayTypeWxXcx {
param.SubAppID = subAppID
authInfo, err := p.Ctx.GetV2AuthInfo()
// 微信小程序支付
if err == nil && authInfo.GetAuthType() == weixin.AuthTypeMini && authInfo.GetAuthTypeID() == subAppID {
param.Acct = authInfo.GetAuthID()
}
}
if p.VendorPayType == tonglianpayapi.PayTypeZfbJS || p.VendorPayType == tonglianpayapi.PayTypeZfbApp {
if authInfo, err := p.Ctx.GetV2AuthInfo(); err == nil {
param.Acct = authInfo.GetAuthID()
}
if param.Acct == "" {
return fmt.Errorf("未找到用户的认证ID")
}
}
if p.VendorPayType == tonglianpayapi.PayTypeH5 {
param2 := &tonglianpayapi.CreateH5UnitorderOrderParam{
Trxamt: p.Order.PayPrice,
NotifyUrl: globals.TLPayNotifyURL,
Body: "冲天猴",
Charset: "UTF-8",
}
err = api.TLpayAPI.CreateH5UnitorderOrder(param2)
} else {
result, err := api.TLpayAPI.CreateUnitorderOrder(param)
if err != nil {
return err
}
var result2 tonglianpayapi.PayInfo
json.Unmarshal([]byte(result.PayInfo), &result2)
p.Order.PrepayID = result2.Package[strings.LastIndex(result2.Package, "=")+1 : len(result2.Package)]
p.Order.TransactionID = result.TrxID
if _, err = dao.UpdateEntityTx(txDB, p.Order, "PrepayID", "TransactionID"); err != nil {
return err
}
wxPay := &WxPayParam{
Prepayid: p.Order.PrepayID,
Noncestr: result2.NonceStr,
Timestamp: utils.Int64ToStr(utils.MustInterface2Int64(result2.TimeStamp)),
Package: result2.Package,
Partnerid: "", // 商户Id
Appid: result2.AppID,
Sign: result2.PaySign,
}
p.WxPayParam = wxPay
}
// 暂时不支持微信直接支付
case model.PayTypeWX:
param := &wxpayapi.CreateOrderParam{
OutTradeNo: p.Order.OrderID,
Body: "冲天猴儿App账户充值",
NotifyURL: globals.WxpayNotifyURL,
SpbillCreateIP: p.Ctx.GetRealRemoteIP(),
TradeType: p.VendorPayType,
TotalFee: p.Order.PayPrice,
TimeStart: wxpayapi.Time2PayTime(time.Now()),
// ProfitSharing: wxpayapi.OptYes,
}
authBinds, err := dao.GetUserBindAuthInfo(dao.GetDB(), p.Ctx.GetUserID(), model.AuthBindTypeAuth, []string{p.Order.Way}, "", "", "")
if err != nil {
return err
}
if len(authBinds) == 0 {
return fmt.Errorf("未绑定微信认证方式!")
}
param.OpenID = authBinds[0].AuthID
result, err2 := payMap[p.Order.Way].CreateUnifiedOrder(param)
if err2 == nil {
param2 := make(map[string]interface{})
param2["prepayid"] = result.PrepayID
param2["noncestr"] = utils.GetUUID()
param2["timestamp"] = time.Now().Unix()
param2["package"] = "Sign=WXPay"
param2["partnerid"] = result.MchID
param2["appid"] = result.AppID
sign := signParam(sigTypeMd5, param2)
wxPay := &WxPayParam{
Prepayid: param2["prepayid"].(string),
Noncestr: param2["noncestr"].(string),
Timestamp: utils.Int64ToStr(utils.MustInterface2Int64(param2["timestamp"])),
Package: param2["package"].(string),
Partnerid: param2["partnerid"].(string),
Appid: param2["appid"].(string),
Sign: sign,
}
p.WxPayParam = wxPay
p.Order.PrepayID = result.PrepayID
p.Order.Comment = result.CodeURL
_, err = dao.UpdateEntityTx(txDB, p.Order, "PrepayID", "Comment")
} else {
return err2
}
default:
err = fmt.Errorf("支付方式:%d当前不支持", p.PayType)
}
return err
}
func signParam(signType string, params map[string]interface{}) (sig string) {
var valueList []string
for k, v := range params {
if k != sigKey {
if str := fmt.Sprint(v); str != "" {
valueList = append(valueList, fmt.Sprintf("%s=%s", k, str))
}
}
}
sort.Sort(sort.StringSlice(valueList))
valueList = append(valueList, fmt.Sprintf("key=%s", globals.WxpayAppKey))
sig = strings.Join(valueList, "&")
var binSig []byte
if signType == sigTypeSha256 {
mac := hmac.New(sha256.New, []byte(globals.WxpayAppKey))
mac.Write([]byte(sig))
binSig = mac.Sum(nil)
} else {
binSig2 := md5.Sum([]byte(sig))
binSig = binSig2[:]
}
sig = fmt.Sprintf("%X", binSig)
// baseapi.SugarLogger.Debug(sig)
return sig
}
func (p *PayHandler) CreateRefund() (err error) {
switch p.PayType {
case model.PayTypeTL: // 收费贵,不适用
case model.PayTypeAliPay: // 支付宝支付
case model.PayTypeWX: // 需要保持账号每天有交易流水,不适用
//企业付款(提现)
if p.VendorPayType == model.VendorPayTypeCompanyPay {
param := &wxpayapi.TransfersParam{
PartnerTradeNo: p.Order.OrderID,
CheckName: wxpayapi.CheckName,
Desc: "冲天猴儿app提现到账",
SpbillCreateIP: p.Ctx.GetRealRemoteIP(),
}
//1元以下免费以上收取对应城市手续费
place, err := dao.GetPlaceByCode(dao.GetDB(), p.Order.CityCode)
if err != nil || place == nil {
return fmt.Errorf("未找到该城市code%v", p.Order.CityCode)
}
if p.Order.PayPrice < 100 {
param.Amount = p.Order.PayPrice
} else {
param.Amount = p.Order.PayPrice * place.DividePercentage / 100 //手续费
}
if authInfo, err := p.Ctx.GetV2AuthInfo(); err == nil {
param.OpenID = authInfo.GetAuthID()
}
globals.SugarLogger.Debugf("CreateRefund wx param: %v", utils.Format4Output(param, false))
result, err2 := payMap[p.Order.Way].Transfers(param)
if err2 == nil {
p.Order.PayFinishedAt = utils.Str2Time(result.PaymentTime)
p.Order.Comment = result.DeviceInfo
p.Order.OriginalData = utils.Format4Output(result, true)
if result.ReturnMsg == "" {
p.Order.Status = model.OrderStatusFinished
} else {
p.Order.Status = model.OrderStatusCanceled
}
dao.UpdateEntity(dao.GetDB(), p.Order)
if result.ReturnMsg == "" {
err = OnCashFinished(p.Order)
}
} else {
return err2
}
err = OnCashFinished(p.Order)
} else if p.VendorPayType == model.VendorPayTypeTransferAccount {
p.Order.PayFinishedAt = time.Now()
p.Order.Comment = "手动转账"
p.Order.Status = model.OrderStatusFinished
if _, err := dao.UpdateEntity(dao.GetDB(), p.Order); err == nil {
err = OnCashFinished(p.Order)
}
}
}
return err
}
func OnTLPayCallback(call *tonglianpayapi.CallBackResult) (err error) {
globals.SugarLogger.Debugf("OnTLPayCallback msg:%s", utils.Format4Output(call, true))
switch call.TrxCode {
case tonglianpayapi.MsgTypePay:
err = onTLpayFinished(call)
case tonglianpayapi.MsgTypeRefund:
err = onTLpayRefund(call)
}
return err
}
func onTLpayFinished(call *tonglianpayapi.CallBackResult) (err error) {
order := &model.Order{
OrderID: call.CusorderID,
}
db := dao.GetDB()
if err = dao.GetEntity(db, order, "OrderID"); err == nil {
if order.Status != model.OrderStatusWait4Pay {
globals.SugarLogger.Debugf("already pay msg:%s, err:%v", utils.Format4Output(call, true), err)
return err
}
loc, _ := time.LoadLocation("Local")
t1, _ := time.ParseInLocation("20060102150405", call.PayTime, loc)
order.PayFinishedAt = t1
order.OriginalData = utils.Format4Output(call, true)
payStatus := 0
order.PayMethod = 2 // 通联微信支付
if call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
order.Status = model.OrderStatusFinished
payStatus = model.OrderStatusSuccessPay
} else {
order.Status = model.OrderStatusCanceled
payStatus = model.OrderStatusFailPay
}
//充值会员 增加微信支付处理业务
if (order.OrderType == 2 || order.OrderType == 5) && call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
return OnWXPayFinished(order)
} else if order.OrderType == 3 {
txdb, _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
panic(r)
}
}()
if _, err := dao.UpdateEntityTx(txdb, order); err != nil {
dao.Rollback(db, txdb)
return err
}
userOrder := model.UserVendorOrder{LocalWayBill: order.OrderID}
if err := dao.GetEntity(db, &userOrder, `LocalWayBill`); err != nil {
dao.Rollback(db, txdb)
return err
}
userOrder.OrderStatus = payStatus
if _, err := dao.UpdateEntityTx(txdb, &userOrder, "OrderStatus"); err != nil {
dao.Rollback(db, txdb)
return err
}
dao.Commit(db, txdb)
if call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
switch order.OrderType {
case model.PayType4Express:
err = q_bida.CreateOrder2QBiDa(&userOrder)
}
}
return err
}
} else {
globals.SugarLogger.Debugf("onTLpayFinished msg:%s, err:%v", utils.Format4Output(call, true), err)
}
return err
}
func onTLpayRefund(call *tonglianpayapi.CallBackResult) (err error) {
orderPayRefund := &model.OrderPayRefund{
RefundID: call.CusorderID,
}
db := dao.GetDB()
if err = dao.GetEntity(db, orderPayRefund, "RefundID"); err == nil {
if call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
orderPayRefund.Status = model.RefundStatusYes
} else {
orderPayRefund.Status = model.RefundStatusFailed
}
orderPayRefund.OriginalData = utils.Format4Output(call, true)
dao.UpdateEntity(db, orderPayRefund)
} else if dao.IsNoRowsError(err) {
globals.SugarLogger.Warnf("收到异常的退款事件, call:%s", utils.Format4Output(call, true))
}
orderPay := &model.Order{
OrderID: orderPayRefund.VendorOrderID,
}
if err = dao.GetEntity(db, orderPay, "VendorOrderID"); err == nil {
orderPay.Status = model.OrderStatusCancel
}
if orderPay.OrderType == model.PayType4Express {
tx, _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, tx)
panic(r)
}
}()
if _, err = dao.UpdateEntityTx(tx, orderPay); err != nil {
tx.Rollback()
return err
}
if _, err = dao.UpdateEntityTx(tx, &model.UserVendorOrder{LocalWayBill: call.CusorderID, OrderStatus: model.OrderStatusCancel}, "OrderStatus"); err != nil {
tx.Rollback()
return err
}
tx.Commit()
return err
}
return err
}
func OnWxPayCallback(msg *wxpayapi.CallbackMsg) (err error) {
globals.SugarLogger.Debugf("OnWxPayCallback msg:%s", utils.Format4Output(msg, true))
switch msg.MsgType {
case wxpayapi.MsgTypePay:
err = onWxpayFinished(msg.Data.(*wxpayapi.PayResultMsg))
case wxpayapi.MsgTypeRefund:
// err = onWxpayRefund(msg.Data.(*wxpayapi.RefundResultMsg))
}
return err
}
func onWxpayFinished(msg *wxpayapi.PayResultMsg) (err error) {
order := &model.Order{
OrderID: msg.OutTradeNo,
}
db := dao.GetDB()
if err = dao.GetEntity(db, order, "OrderID"); err == nil {
order.PayFinishedAt = wxpayapi.PayTime2Time(msg.TimeEnd)
order.TransactionID = msg.TransactionID
order.OriginalData = utils.Format4Output(msg, true)
if msg.ResultCode == wxpayapi.ResponseCodeSuccess {
order.Status = model.OrderStatusFinished
} else {
order.Status = model.OrderStatusCanceled
}
dao.UpdateEntity(db, order)
if msg.ResultCode == wxpayapi.ResponseCodeSuccess {
err = OnPayFinished(order)
}
} else {
globals.SugarLogger.Debugf("onWxpayFinished msg:%s, err:%v", utils.Format4Output(msg, true), err)
}
return err
}
// func onWxpayRefund(msg *wxpayapi.RefundResultMsg) (err error) {
// orderPayRefund := &model.OrderPayRefund{
// RefundID: msg.ReqInfoObj.OutRefundNo,
// }
// db := dao.GetDB()
// if err = dao.GetEntity(db, orderPayRefund, "RefundID"); err == nil {
// if msg.ResultCode == wxpayapi.ResponseCodeSuccess {
// orderPayRefund.Status = model.RefundStatusYes
// } else {
// orderPayRefund.Status = model.RefundStatusFailed
// }
// orderPayRefund.OriginalData = utils.Format4Output(msg, true)
// dao.UpdateEntity(db, orderPayRefund)
// } else if dao.IsNoRowsError(err) {
// globals.SugarLogger.Warnf("收到异常的退款事件, msg:%s", utils.Format4Output(msg, true))
// }
// orderPay := &model.OrderPay{
// VendorOrderID: orderPayRefund.VendorOrderID,
// VendorID: jxutils.GetPossibleVendorIDFromVendorOrderID(orderPayRefund.VendorOrderID),
// PayType: model.PayTypeWX,
// Status: model.PayStatusYes,
// }
// orderPay.DeletedAt = utils.DefaultTimeValue
// if err = dao.GetEntity(db, orderPay, "VendorOrderID", "VendorID", "PayType", "Status", "DeletedAt"); err == nil {
// orderPay.Status = model.PayStatusRefund
// dao.UpdateEntity(db, orderPay)
// }
// return err
// }