422 lines
13 KiB
Go
422 lines
13 KiB
Go
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
|
||
// }
|