| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 | 
							- package shanghu
 
- import (
 
- 	"duoduo/apis/common"
 
- 	"duoduo/apis/shanghu/models"
 
- 	"duoduo/models/shanghu"
 
- 	"duoduo/tools"
 
- 	"duoduo/tools/app"
 
- 	"encoding/xml"
 
- 	"errors"
 
- 	"fmt"
 
- 	"github.com/gin-gonic/gin"
 
- 	"github.com/go-pay/gopay"
 
- 	"github.com/go-pay/gopay/wechat"
 
- 	wechatV3 "github.com/go-pay/gopay/wechat/v3"
 
- 	"github.com/shopspring/decimal"
 
- 	"net/http"
 
- 	"reflect"
 
- 	"strconv"
 
- 	"time"
 
- )
 
- type wechatCallbackResp struct {
 
- 	XMLName    xml.Name `xml:"xml"`
 
- 	ReturnCode Cdata    `xml:"return_code"`
 
- 	ReturnMsg  Cdata    `xml:"return_msg"`
 
- }
 
- type Cdata struct {
 
- 	Value string `xml:",cdata"`
 
- }
 
- var (
 
- 	successResp = &wechatCallbackResp{ReturnCode: Cdata{Value: "SUCCESS"}, ReturnMsg: Cdata{Value: "OK"}}
 
- 	failResp    = &wechatCallbackResp{ReturnCode: Cdata{Value: "FAIL"}, ReturnMsg: Cdata{Value: "数据处理异常"}}
 
- )
 
- func UnifiedOrder(c *gin.Context) {
 
- 	var inData models.UnifiedOrderRequest
 
- 	var sqlData shanghu.ClientPayTrans
 
- 	var outData models.UnifiedOrderReply
 
- 	err := c.ShouldBindJSON(&inData)
 
- 	if err != nil {
 
- 		app.Error(c, 400, err, err.Error())
 
- 		return
 
- 	}
 
- 	//校验 防止同一笔记录存在
 
- 	sqlData.RequestID = inData.RequestId
 
- 	if sqlData.GetRequestNum() > 0 {
 
- 		app.Error(c, 400, errors.New("交易已存在"), "交易已存在")
 
- 		return
 
- 	}
 
- 	//校验金额
 
- 	// 检查微信相关参数
 
- 	if !inData.Amount.GreaterThan(decimal.NewFromInt(0)) {
 
- 		app.Error(c, 400, errors.New("amount:金额必须大于0"), "amount:金额必须大于0")
 
- 		return
 
- 	}
 
- 	if inData.Amount.Round(2).String() != inData.Amount.String() {
 
- 		app.Error(c, 400, errors.New("total_fee:金额最多只能保留两位小数"), "total_fee:金额最多只能保留两位小数")
 
- 		return
 
- 	}
 
- 	//创建支付记录
 
- 	sqlData.ClientOpenID = inData.ClientOpenId
 
- 	sqlData.RequestID = inData.RequestId
 
- 	sqlData.CreatedAt = time.Now()
 
- 	sqlData.UpdatedAt = time.Now()
 
- 	sqlData.Amount = inData.Amount
 
- 	sqlData.OutTradeNo = strconv.FormatInt(inData.MerchantCardId, 10) + strconv.FormatInt(time.Now().UnixNano(), 10)
 
- 	sqlData.Status = 1 //未支付
 
- 	sqlData.MerchantCardID = inData.MerchantCardId
 
- 	sqlData.InvitationCode = inData.InvitationCode
 
- 	_, err = sqlData.Create()
 
- 	if err != nil {
 
- 		app.Error(c, 400, err, "创建支付失败")
 
- 		return
 
- 	}
 
- 	fmt.Println(sqlData.OutTradeNo)
 
- 	bm := make(gopay.BodyMap)
 
- 	bm.Set("nonce_str", common.GetRandomString(32))
 
- 	bm.Set("body", "商户卡")
 
- 	bm.Set("out_trade_no", sqlData.OutTradeNo)
 
- 	bm.Set("total_fee", inData.Amount.Mul(decimal.NewFromInt(100)).IntPart())
 
- 	bm.Set("spbill_create_ip", "127.0.0.1")
 
- 	bm.Set("notify_url", "https://shisanmiao.com/v1/client/pay/callback")
 
- 	bm.Set("device_info", "WEB")
 
- 	bm.Set("trade_type", "JSAPI")
 
- 	bm.Set("sign_type", wechat.SignType_MD5)
 
- 	bm.Set("openid", inData.ClientOpenId)
 
- 	client := NewWechatService()
 
- 	//请求支付下单,成功后得到结果
 
- 	wxResp, err := client.UnifiedOrder(c, bm)
 
- 	if err != nil {
 
- 		app.Error(c, 400, err, "下单失败")
 
- 		return
 
- 	}
 
- 	if wxResp.ReturnCode != "SUCCESS" {
 
- 		app.Error(c, 400, errors.New(wxResp.ReturnMsg), "下单失败")
 
- 		return
 
- 	}
 
- 	if wxResp.ResultCode != "SUCCESS" {
 
- 		app.Error(c, 400, errors.New(wxResp.ErrCode+"--"+wxResp.ErrCodeDes), "下单失败")
 
- 		return
 
- 	}
 
- 	timestamp := strconv.FormatInt(time.Now().Unix(), 10)
 
- 	pac := "prepay_id=" + wxResp.PrepayId
 
- 	paySign := wechat.GetMiniPaySign("wx25357518f710b8ce", wxResp.NonceStr, pac, wechat.SignType_MD5, timestamp, "1RKRJBVH4vaRrF0XPW9GX2M3ZSImukIz")
 
- 	outData.Timestamp = timestamp
 
- 	outData.NonceStr = wxResp.NonceStr
 
- 	outData.Package = pac
 
- 	outData.PaySign = paySign
 
- 	outData.SignType = wechat.SignType_MD5
 
- 	//merchant, count, err := sqlData.GetOpenIdList(pageSize, pageIndex)
 
- 	//if err != nil {
 
- 	//	app.Error(c, 500, err, err.Error())
 
- 	//	return
 
- 	//}
 
- 	app.OK(c, outData, app.Success)
 
- }
 
- func PayCashOut(c *gin.Context) {
 
- 	var inData models.PayCashOutRequest
 
- 	var merchantAccountSql shanghu.MerchantAccount
 
- 	var clientAccountSql shanghu.MerchantClientAccount
 
- 	var cashOut shanghu.CashOut
 
- 	var trans []models.TransferDetailList
 
- 	var transDetail models.TransferDetailList
 
- 	err := c.ShouldBindJSON(&inData)
 
- 	if err != nil {
 
- 		app.Error(c, 400, err, err.Error())
 
- 		return
 
- 	}
 
- 	if inData.Appid == "" { //appid 不能为空
 
- 		app.Error(c, 400, errors.New("Appid不能为空"), "Appid不能为空")
 
- 		return
 
- 	}
 
- 	if inData.Amount.Cmp(decimal.NewFromInt(500)) > 0 {
 
- 		app.Error(c, 400, errors.New("单笔金额不能大于500"), "单笔金额不能大于500")
 
- 		return
 
- 	}
 
- 	if inData.Amount.Cmp(decimal.NewFromInt(1)) < 0 {
 
- 		app.Error(c, 400, errors.New("单笔金额不能小于1"), "单笔金额不能大于1")
 
- 		return
 
- 	}
 
- 	//
 
- 	cashOut.OpenID = inData.OpenId
 
- 	cashOut.AppID = inData.Appid
 
- 	cashOut.Status = 1    //提现中
 
- 	status := []int{1, 3} //提现中、金额待扣减
 
- 	cashNum := cashOut.GetCashOutByStatusNum(status)
 
- 	if cashNum > 0 {
 
- 		app.Error(c, 400, errors.New("有一笔交易正在提现中"), "有一笔交易正在提现中")
 
- 		return
 
- 	}
 
- 	//校验金额是否够
 
- 	if inData.AccountType == "client" {
 
- 		clientAccountSql.ClientOpenID = inData.OpenId
 
- 		clientAccountInfo, err := clientAccountSql.GetClientAccount()
 
- 		if err != nil {
 
- 			app.Error(c, 400, err, err.Error())
 
- 			return
 
- 		}
 
- 		if inData.Amount.Cmp(clientAccountInfo.Amount) > 0 {
 
- 			app.Error(c, 400, errors.New("账号余额不够"), "账号余额不够")
 
- 			return
 
- 		}
 
- 		var client shanghu.MerchantClientUser
 
- 		client.Code = "7jb6"
 
- 		clientInfo, _ := client.GetUserInfoByCode()
 
- 		if clientInfo.ClientOpenID == inData.OpenId {
 
- 			cashOut.Fee = decimal.NewFromInt(0)
 
- 		} else {
 
- 			cashOut.Fee = inData.Amount.Mul(decimal.NewFromFloat32(0.05))
 
- 		}
 
- 	} else if inData.AccountType == "merchant" {
 
- 		merchantAccountSql.MerchantOpenID = inData.OpenId
 
- 		merchantAccountInfo, err := merchantAccountSql.GetMerchantAccount()
 
- 		if err != nil {
 
- 			app.Error(c, 400, err, err.Error())
 
- 			return
 
- 		}
 
- 		if inData.Amount.Cmp(merchantAccountInfo.Amount) > 0 {
 
- 			app.Error(c, 400, errors.New("账号余额不够"), "账号余额不够")
 
- 			return
 
- 		}
 
- 		cashOut.Fee = decimal.NewFromInt(0)
 
- 	} else {
 
- 		app.Error(c, 400, errors.New("账户类型错误"), "账户类型错误")
 
- 		return
 
- 	}
 
- 	clientV3, err := NewWechatServiceV3(inData.Appid)
 
- 	if err != nil {
 
- 		app.Error(c, 400, err, err.Error())
 
- 		return
 
- 	}
 
- 	userName, err := clientV3.V3EncryptText(inData.UserName)
 
- 	if err != nil {
 
- 		app.Error(c, 400, err, err.Error())
 
- 		return
 
- 	}
 
- 	transDetail.OutDetailNo = common.GetRandomString(32)
 
- 	transDetail.TransferAmount = inData.Amount.Sub(cashOut.Fee).Mul(decimal.NewFromInt(100)).IntPart()
 
- 	transDetail.UserName = userName
 
- 	transDetail.Openid = inData.OpenId
 
- 	transDetail.TransferRemark = "提现"
 
- 	trans = append(trans, transDetail)
 
- 	partnerTradeNo := common.GetRandomString(32)
 
- 	var cashOutCreate shanghu.CashOut
 
- 	//创建提现记录
 
- 	cashOutCreate.AppID = inData.Appid
 
- 	cashOutCreate.Status = 1 //提现中
 
- 	cashOutCreate.OpenID = inData.OpenId
 
- 	cashOutCreate.Amount = inData.Amount
 
- 	cashOutCreate.CreatedAt = time.Now()
 
- 	cashOutCreate.UpdatedAt = time.Now()
 
- 	cashOutCreate.PartnerTradeNo = partnerTradeNo
 
- 	cashOutCreate.Fee = cashOut.Fee
 
- 	cashOutInfo, err := cashOutCreate.Create()
 
- 	if err != nil {
 
- 		app.Error(c, 400, err, err.Error())
 
- 		return
 
- 	}
 
- 	var bMap []gopay.BodyMap
 
- 	bm := make(gopay.BodyMap)
 
- 	bm.Set("appid", inData.Appid)
 
- 	bm.Set("out_batch_no", partnerTradeNo)
 
- 	bm.Set("batch_name", "体现")
 
- 	bm.Set("batch_remark", "提现")
 
- 	bm.Set("total_amount", inData.Amount.Sub(cashOut.Fee).Mul(decimal.NewFromInt(100)).IntPart())
 
- 	bm.Set("total_num", 1)
 
- 	bMap = append(bMap, structToMap(&transDetail))
 
- 	bm.Set("transfer_detail_list", bMap)
 
- 	bm.Set("transfer_scene_id", "1001")
 
- 	fmt.Println(bm.JsonBody())
 
- 	reply, err := clientV3.V3Transfer(c, bm)
 
- 	if err != nil {
 
- 		cashOut.ID = cashOutInfo.ID
 
- 		cashOut.FailRes = err.Error()
 
- 		cashOut.Status = 2 //提现失败
 
- 		cashOut.UpdateMerchantStatus()
 
- 		app.Error(c, 500, err, err.Error())
 
- 		return
 
- 	}
 
- 	if reply.Code != 0 {
 
- 		cashOut.ID = cashOutInfo.ID
 
- 		cashOut.FailRes = reply.Error
 
- 		cashOut.Status = 2 //提现失败
 
- 		cashOut.UpdateMerchantStatus()
 
- 		app.Error(c, 500, errors.New(reply.Error), reply.Error)
 
- 		return
 
- 	}
 
- 	cashOut.WxPartnerTradeNo = reply.Response.BatchId
 
- 	cashOut.PartnerTradeNo = reply.Response.OutBatchNo
 
- 	cashOut.UpdateCashOutWxBachNo()
 
- 	app.OK(c, nil, app.Success)
 
- }
 
- func PayCallBack(c *gin.Context) {
 
- 	var payLog shanghu.PayCallbackLog
 
- 	var payTrans shanghu.ClientPayTrans
 
- 	wxNotify, err := wechat.ParseNotifyToBodyMap(c.Request)
 
- 	if err != nil {
 
- 		c.XML(http.StatusOK, failResp)
 
- 		return
 
- 	}
 
- 	//通知回调log
 
- 	payLog.CallBackLog = wxNotify.JsonBody()
 
- 	payLog.ThirdTradeNo = wxNotify.Get("transaction_id")
 
- 	payLog.OutTradeNo = wxNotify.Get("out_trade_no")
 
- 	payLog.CreatedAt = time.Now()
 
- 	payLog.UpdatedAt = time.Now()
 
- 	_, err = payLog.Create()
 
- 	if err != nil {
 
- 		c.XML(http.StatusOK, failResp)
 
- 		return
 
- 	}
 
- 	if wxNotify.Get("return_code") != "SUCCESS" || wxNotify.Get("result_code") != "SUCCESS" {
 
- 		payLog.ErrLog = "微信返回错误:" + wxNotify.Get("return_code") + "--" + wxNotify.Get("result_code")
 
- 		payLog.UpdateMerchant()
 
- 		c.XML(http.StatusOK, failResp)
 
- 		return
 
- 	}
 
- 	//校验金额
 
- 	payTrans.OutTradeNo = wxNotify.Get("out_trade_no")
 
- 	payTransInfo, err := payTrans.GetPayTransByTradeNo()
 
- 	if err != nil {
 
- 		payLog.ErrLog = "查询交易信息错误:" + " err=" + err.Error()
 
- 		payLog.UpdateMerchant()
 
- 		c.XML(http.StatusOK, failResp)
 
- 		return
 
- 	}
 
- 	// 判断金额与支付流水是否一致
 
- 	totalFee, err := decimal.NewFromString(wxNotify.Get("total_fee"))
 
- 	if err != nil {
 
- 		payLog.ErrLog = "解析总金额报错:" + "err=" + err.Error()
 
- 		payLog.UpdateMerchant()
 
- 		c.XML(http.StatusOK, failResp)
 
- 		return
 
- 	}
 
- 	if !totalFee.Equal(payTransInfo.Amount.Mul(decimal.NewFromInt(100))) {
 
- 		payLog.ErrLog = "验证金额报错:total_fee=" + wxNotify.Get("total_fee") + " amount=" + payTransInfo.Amount.String()
 
- 		payLog.UpdateMerchant()
 
- 		c.XML(http.StatusOK, failResp)
 
- 		return
 
- 	}
 
- 	// 解析支付时间
 
- 	timeEnd, err := time.ParseInLocation("20060102150405", wxNotify.Get("time_end"), tools.TimeLocation)
 
- 	if err != nil {
 
- 		payLog.ErrLog = "付款时间解析出错:err=" + err.Error()
 
- 		payLog.UpdateMerchant()
 
- 		c.XML(http.StatusOK, failResp)
 
- 		return
 
- 	}
 
- 	payTrans.ThirdTradeNo = wxNotify.Get("transaction_id")
 
- 	payTrans.Status = 2 //支付成功
 
- 	payTrans.PayTime = timeEnd
 
- 	payTrans.AccountStatus = 1 //未分账
 
- 	err = payTrans.UpdatePayTransByTradeNo()
 
- 	if err != nil {
 
- 		payLog.ErrLog = "更新支付状态失败:err=" + err.Error()
 
- 		payLog.UpdateMerchant()
 
- 		c.XML(http.StatusOK, failResp)
 
- 		return
 
- 	}
 
- 	c.XML(http.StatusOK, successResp)
 
- }
 
- func NewWechatService() *wechat.Client {
 
- 	client := wechat.NewClient("wx25357518f710b8ce", "1501641641", "1RKRJBVH4vaRrF0XPW9GX2M3ZSImukIz", true)
 
- 	//设置国家
 
- 	client.SetCountry(wechat.China)
 
- 	return client
 
- }
 
- func NewWechatServiceAppid(appid string) *wechat.Client {
 
- 	client := wechat.NewClient(appid, "1501641641", "1RKRJBVH4vaRrF0XPW9GX2M3ZSImukIz", true)
 
- 	//设置国家
 
- 	client.SetCountry(wechat.China)
 
- 	return client
 
- }
 
- func NewWechatServiceV3(appid string) (*wechatV3.ClientV3, error) {
 
- 	key := `-----BEGIN PRIVATE KEY-----
 
- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD/RTrS1Tycrskr
 
- SkuzdLNmOic4UklFtoyKtKDIeklbQFvynJBC1wYaitoOqFkdGWagkcuqRqJOcgEQ
 
- T8DRDD/rnVKTrnntevUSW3vZYwMscomK3dgjRRui5GLY5wYHuKhp02bldLrhPKNy
 
- wT5IYhoDwQLmf5IrURbtT8bKpdC+NyEEfw+d/mHMK56w80YKfzFtSmjoB9SkWDLg
 
- YFBlw91C78E73uqhCKnArN7p2iJe+tsp7EGpDVcdowqcMKjY2YiNPY6ABZaI9YJx
 
- nRQs5Sjm6YSsJujMR5rQVZ3HCfLlzxLpPyHfqffbJ+vviend1PJ/ugJeM2hehn1d
 
- LrpKuWyHAgMBAAECggEAIbow6nhYGM+TLtATLnPF3ETkt7FPkxFqgk0ZTUOy+4aG
 
- X4jGGr60RL+Bzhv6Ijkf8SkyQp4whbLUZyZScIxwyZ2wsmiEHZd4V+OUeoV1fuLn
 
- P1zOWOKhoyUP1l630j9YqRrQZpLEuku7wMa9huzHSSWwT2odkvGU2OgIeO/to6P7
 
- gIE4Q+3pNmTdaXJweUlVoz6sdqsSWAZ729SxzY34zcRyXI+feBQb9n/C1hr9+Ge4
 
- KbBsvrECy0eUNiAT9f5dPwgwjyRpR9gJ8xxaezIYfxdpTTW3EB1Gf+xPX4X7ml2B
 
- zObPN2HEwLHhrwbOXLFP8F/62TK4fIGe2yXPIKIe8QKBgQD/t4Xy9pTFrmNhDLMe
 
- ZMHdj9XMoM/izcquI6rq4pGWLtnyxIcFuXiqvPszbJL+urCc1+HqfPAVScryEg5W
 
- b6AvLENVhC5E3GcZA50ciAr+PUrB1UXH0vbbc7HP/Mofsz61kxEW2SvKaPoKhW8M
 
- IavoVWDJkcLHnjM20Gt7tioEXwKBgQD/jZR7FDSB8VCRp687QcH4s0HyAWaRVhmC
 
- qId2xVbPFVlbm4BWcQVWPdsUei29sPPtcvzREFStKwTmu2FSUfoDv3uM4Nl2+h/Y
 
- pbhfuYfHd79UZa0OtT9njJHZcYPwKRmZQIVBVHSJFSZy2HSGLe9AdI7gAsNMx/Mm
 
- eqZDU79I2QKBgQCBSNoSIpTI9QgNkwwkO7DAQe5IDK3N71mffSz2oCIXGgza7n2N
 
- aV4WhIFEWIpg+yY7xfHUSeJgAPT4OiTBkqIb93b7j16NNhlxzh/qwuU78OUQ5rDm
 
- /EQOY4nsq9PM/ySfTIGBWb8IENcJ5rhkG8n8Jt5OSsF9hwBBoFIXM9w+ZQKBgQD1
 
- rArw43Sy8uTskZKA2e96ggHEgBo1X9s4Y4GO6ZlRjQmRaoVPFGn4BZEGN4qfkGx/
 
- egqXhSaSLwgQNFUUCWDbl4pT3ZjRqxVQdcgwpjBkzratkO10dUOV7WoM6vbWuvwz
 
- +vXf3ywE2MNUpsgmciROB3+O1LkhqBsVg9UwZmM+yQKBgQD6yBvxOaHXBSZncz+m
 
- 3SpdbcRjK82i3IUJ3sl18J7YEPer0FclsUcQOluqBbHTOAr8letWPrKnjZGuyFfg
 
- gAjwa8uLSyfEEcWb9WcObvud2GNxS/LiI0GnW9ittvT29JvOhmUUtnty5/TiWIsi
 
- slm2kO53RSw9brymV8PAX2+SXg==
 
- -----END PRIVATE KEY-----`
 
- 	client, err := wechatV3.NewClientV3("1501641641", "219B3AF3B5F17D4C2F145EE318188708318DD7BD", "9x9ydkdk0nzsa4mr2ucq75grlvt9n8l3", key)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	// 启用自动同步返回验签,并定时更新微信平台API证书(开启自动验签时,无需单独设置微信平台API证书和序列号)
 
- 	err = client.AutoVerifySign()
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	// 打开Debug开关,输出日志,默认是关闭的
 
- 	//client.DebugSwitch = gopay.DebugOn
 
- 	return client, nil
 
- }
 
- func structToMap(obj interface{}) gopay.BodyMap {
 
- 	result := make(gopay.BodyMap)
 
- 	value := reflect.ValueOf(obj).Elem() // 获取指针的值
 
- 	typ := value.Type()                  // 获取类型信息
 
- 	for i := 0; i < typ.NumField(); i++ {
 
- 		field := typ.Field(i)              // 获取字段信息
 
- 		tag := field.Tag.Get("json")       // 获取标签(如果有)
 
- 		if tag != "" && !field.Anonymous { // 只处理非匿名字段且有标签的情况
 
- 			key := field.Name // 默认使用字段名作为Key
 
- 			if tag != "-" {   // 若标签不等于-则使用标签作为Key
 
- 				key = tag
 
- 			}
 
- 			result[key] = value.Field(i).Interface() // 存入Map
 
- 		}
 
- 	}
 
- 	return result
 
- }
 
 
  |