| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- package wx_mp
- import (
- // "encoding/json"
- "encoding/xml"
- "fmt"
- "fohow.com/apps/models/order_model"
- // "io/ioutil"
- // "net/http"
- "reflect"
- "strings"
- "time"
- "github.com/astaxie/beego"
- "github.com/uuid"
- // "github.com/astaxie/beego/context"
- "github.com/chanxuehong/wechat/mch"
- // "github.com/chanxuehong/wechat/mch/mmpaymkttransfers"
- "github.com/chanxuehong/wechat/mch/mmpaymkttransfers/promotion"
- "github.com/chanxuehong/wechat/mch/pay"
- // "github.com/chanxuehong/wechat/mp/user"
- // "go.xikego.com/apps/models/shop_model"
- // "go.xikego.com/cache"
- )
- const (
- //玖玖好购
- mchId = "1604238155"
- apiKey = "w7gb9ur5hb9g5326vy3sr0q4cobmrpgq"
- //乐福优选
- lehuMchId = "1581904571"
- lehuApiKey = "r1sbdza2mgs2zo3x2u6w8plgh91z0gqq"
- //凤凰欧标
- oubiaoMchId = "1601075858"
- oubiaoApiKey = "7592dac80b33214829b3484740162ac4"
- //丰和
- fengheMchId = "1616910264"
- fengheApiKey = "9WVsj03BL10ych7UlwmDmHhrLfoFqZqq"
- //微信公众号使用小程序的商户号和api加密
- gzhMchId = ""
- gzhApiKey = ""
- PAY_SUCCESS = "SUCCESS"
- PAY_FAIL = "FAIL"
- // NO_CHECK:不校验真实姓名
- // FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账)
- // OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
- PAY_FORCE_CHECK = "FORCE_CHECK"
- PAY_NO_CHECK = "NO_CHECK"
- PAY_OPTION_CHECK = "OPTION_CHECK"
- )
- // 小程序端微信支付client
- func getMchProxyInitClient(payCode, source string) *mch.Proxy {
- mechant := GetMechantInfo(payCode)
- appId := ""
- switch source {
- case order_model.SOURCE_APP:
- appId = beego.AppConfig.String("WxFohowAndroAppAppId") //获取小程序渠道appId
- default:
- appId = beego.AppConfig.String("WxFohowXcxAppId") //获取小程序渠道appId
- }
- mchProxy := mch.NewProxy(appId, mechant.MchId, mechant.ApiKey, nil)
- return mchProxy
- }
- func getMchTLSProxyInitClient(payCode string) *mch.Proxy {
- appId := beego.AppConfig.String("WxFohowXcxAppId") //获取小程序渠道appId
- mechant := GetMechantInfo(payCode)
- var tlsHttpCilent, _ = mch.NewTLSHttpClient(
- mechant.MchCertFile,
- mechant.MchKeyFile)
- mchTLSProxy := mch.NewProxy(appId, mechant.MchId, mechant.ApiKey, tlsHttpCilent)
- return mchTLSProxy
- }
- // 商户渠道信息
- type MechantPayInfo struct {
- MchId string //商户号
- ApiKey string //api密钥
- MchKeyFile string //私钥路径
- MchCertFile string //证书路径
- }
- // 支付结果通知参数
- type PayResult struct {
- XMLName xml.Name `xml:"xml"`
- ReturnCode string `xml:"return_code"` //返回状态码
- ReturnMsg string `xml:"return_msg"` //返回信息
- AppId string `xml:"appid"` //公众账号ID
- MchId string `xml:"mch_id"` //商户号
- NonceStr string `xml:"nonce_str"` //随机字符串
- Sign string `xml:"sign"` //签名
- SignType string `xml:"sign_type"` //签名类型
- ResultCode string `xml:"result_code"` //业务结果
- ErrCode string `xml:"err_code"` //错误代码
- ErrCodeDesc string `xml:"err_code_des"` //错误代码描述
- DeviceInfo string `xml:"device_info"` //设备号
- OpenId string `xml:"openid"` //用户标识
- IsSubscribe string `xml:"is_subscribe"` //是否关注公众账号 Y/N
- TradeType string `xml:"trade_type"` //交易类型 JSAPI、NATIVE、APP
- BankType string `xml:"bank_type"` //付款银行
- TotalFee string `xml:"total_fee"` //总金额
- SettlementTotalFee string `xml:"settlement_total_fee"` //应结订单金额
- FeeType string `xml:"fee_type"` //货币种类
- CashFee string `xml:"cash_fee"` //cash_fee
- CashFeeType string `xml:"cash_fee_type"` //现金支付货币类型
- CouponFee string `xml:"coupon_fee"` //提货券或立减优惠金额
- CouponCount string `xml:"coupon_count"` //提货券或立减优惠使用数量
- TransactionId string `xml:"transaction_id"` //微信支付订单号
- OutTradeNO string `xml:"out_trade_no"` //商户订单号
- Attach string `xml:"attach"` // 商家数据包
- TimeEnd string `xml:"time_end"` // 支付完成时间 yyyyMMddHHmmss
- //coupon_id_$n
- //coupon_fee_$na
- //新增 coupon_fee
- CouponFee0 string `xml:"coupon_fee_0"` //单个提货券支付金额,$n为下标,从0开始编号
- CouponId0 string `xml:"coupon_id_0"` //提货券ID,$n为下标,从0开始编号
- CouponFee1 string `xml:"coupon_fee_1"` //单个提货券支付金额,$n为下标,从0开始编号
- CouponId1 string `xml:"coupon_id_1"` //提货券ID,$n为下标,从0开始编号
- CouponFee2 string `xml:"coupon_fee_2"` //单个提货券支付金额,$n为下标,从0开始编号
- CouponId2 string `xml:"coupon_id_2"` //提货券ID,$n为下标,从0开始编号
- CouponFee3 string `xml:"coupon_fee_3"` //单个提货券支付金额,$n为下标,从0开始编号
- CouponId3 string `xml:"coupon_id_3"` //提货券ID,$n为下标,从0开始编号
- }
- // 获取jsSDK微信支付需要的数据
- func GetMechantInfo(mechantCode string) (ret MechantPayInfo) {
- switch mechantCode {
- case "fohow":
- ret.MchId = mchId
- ret.ApiKey = apiKey
- ret.MchKeyFile = beego.AppConfig.String("WxMchKeyFile")
- ret.MchCertFile = beego.AppConfig.String("MchCertFile")
- case "app":
- ret.MchId = mchId
- ret.ApiKey = apiKey
- ret.MchKeyFile = beego.AppConfig.String("WxMchKeyFile")
- ret.MchCertFile = beego.AppConfig.String("MchCertFile")
- case "lehu":
- ret.MchId = lehuMchId
- ret.ApiKey = lehuApiKey
- ret.MchKeyFile = beego.AppConfig.String("LehuMchKeyFile")
- ret.MchCertFile = beego.AppConfig.String("LehuMchCertFile")
- case "oubiao":
- ret.MchId = oubiaoMchId
- ret.ApiKey = oubiaoApiKey
- ret.MchKeyFile = beego.AppConfig.String("OuBiaoMchKeyFile")
- ret.MchCertFile = beego.AppConfig.String("OuBiaoMchCertFile")
- case "fenghe":
- ret.MchId = fengheMchId
- ret.ApiKey = fengheApiKey
- ret.MchKeyFile = beego.AppConfig.String("FengHeMchKeyFile")
- ret.MchCertFile = beego.AppConfig.String("FengHeMchCertFile")
- default:
- //ret.MchId = mchId
- //ret.ApiKey = apiKey
- //ret.MchKeyFile = beego.AppConfig.String("WxMchKeyFile")
- //ret.MchCertFile = beego.AppConfig.String("MchCertFile")
- ret.MchId = oubiaoMchId
- ret.ApiKey = oubiaoApiKey
- ret.MchKeyFile = beego.AppConfig.String("OuBiaoMchKeyFile")
- ret.MchCertFile = beego.AppConfig.String("OuBiaoMchCertFile")
- }
- return ret
- }
- // 获取APP微信支付需要的数据
- func GetAppPayData(outTradeNo string, totalPrice int64, body, notifyUrl, remoteIp, payCode string) (ret map[string]string) {
- appId := beego.AppConfig.String("WxFohowAndroAppAppId") //获取App appId
- mechantInfo := GetMechantInfo(payCode)
- outTradeNo = fmt.Sprintf("%s_%d", outTradeNo, time.Now().Unix())
- prepayId := getAppPayPrepayId(outTradeNo, totalPrice, body, notifyUrl, remoteIp, payCode, true)
- u := uuid.NewV4().String()
- us := strings.Split(u, "-")
- nonce_str := strings.Join(us, "")
- ret = map[string]string{
- "appid": appId,
- "partnerid": mechantInfo.MchId,
- "prepayid": prepayId,
- "package": "Sign=WXPay",
- "timestamp": fmt.Sprintf("%d", time.Now().Unix()),
- "noncestr": nonce_str}
- ret["pasignySign"] = mch.Sign(ret, mechantInfo.ApiKey, nil)
- beego.BeeLogger.Warn("ret--%v", ret)
- return ret
- }
- // 获取支付预授权码
- //
- // [pay.go:76] [E] GetPrepayId err[return_code: "FAIL", return_msg: "不识别的参数wxappid"]
- //
- // 2016/07/07 11:07:02 [pay_controller.go:303] [W] 生成微信支付订单号:DS20160707503D648C,传的参数:openid=o7lR2txmVPh1pSPjIvto2LO_XVYU,
- // 价格:9.90, 名称:希客购, 客户端IP=119.132.31.240;生成的参数:%!v(MISSING)
- func getAppPayPrepayId(outTradeNo string, totalPrice int64, body, notifyUrl, remoteIp, payCode string, limit bool) (prepayId string) {
- appId := beego.AppConfig.String("WxFohowAndroAppAppId") //获取App渠道appId
- mechantInfo := GetMechantInfo(payCode) //商户号配置信息
- mchProxy := getMchProxyInitClient(payCode, order_model.SOURCE_APP)
- u := uuid.NewV4().String()
- us := strings.Split(u, "-")
- nonce_str := strings.Join(us, "")
- req := map[string]string{
- "mch_id": mechantInfo.MchId,
- "appid": appId,
- "nonce_str": nonce_str, //fmt.Sprintf("%d", time.Now().Unix()),
- "body": body,
- "out_trade_no": outTradeNo,
- "total_fee": fmt.Sprintf("%d", totalPrice), //分为单位
- "spbill_create_ip": remoteIp,
- "notify_url": notifyUrl,
- // "product_id": outTradeNo,
- "trade_type": "APP",
- }
- //beego.BeeLogger.Warn("mechantInfo err[%s]", mechantInfo.MchId)
- if limit {
- req["limit_pay"] = "no_credit"
- }
- sign := mch.Sign(req, mechantInfo.ApiKey, nil)
- req["sign"] = sign
- beego.BeeLogger.Warn("%v", req)
- ret, err := pay.UnifiedOrder(mchProxy, req)
- if err != nil {
- beego.BeeLogger.Error("getAppPayPrepayId err[%s]", err)
- //fmt.Println(err)
- return ""
- }
- return ret["prepay_id"]
- }
- // 获取jsSDK微信支付需要的数据
- func GetPayData(openid, outTradeNo string, totalPrice int64, body, notifyUrl, remoteIp, payCode string) (ret map[string]string) {
- appId := beego.AppConfig.String("WxFohowXcxAppId") //获取小程序渠道appId
- mechantInfo := GetMechantInfo(payCode)
- prepayId := getPayPrepayId(openid, outTradeNo, totalPrice, body, notifyUrl, remoteIp, payCode, true)
- u := uuid.NewV4().String()
- us := strings.Split(u, "-")
- nonce_str := strings.Join(us, "")
- ret = map[string]string{
- "appId": appId,
- "timeStamp": fmt.Sprintf("%d", time.Now().Unix()),
- "package": fmt.Sprintf("prepay_id=%s", prepayId),
- "signType": "MD5",
- "nonceStr": nonce_str}
- ret["paySign"] = mch.Sign(ret, mechantInfo.ApiKey, nil)
- return ret
- }
- // 获取支付预授权码
- //
- // [pay.go:76] [E] GetPrepayId err[return_code: "FAIL", return_msg: "不识别的参数wxappid"]
- //
- // 2016/07/07 11:07:02 [pay_controller.go:303] [W] 生成微信支付订单号:DS20160707503D648C,传的参数:openid=o7lR2txmVPh1pSPjIvto2LO_XVYU,
- // 价格:9.90, 名称:希客购, 客户端IP=119.132.31.240;生成的参数:%!v(MISSING)
- func getPayPrepayId(openid, outTradeNo string, totalPrice int64, body, notifyUrl, remoteIp, payCode string, limit bool) (prepayId string) {
- appId := beego.AppConfig.String("WxFohowXcxAppId") //获取小程序渠道appId
- mechantInfo := GetMechantInfo(payCode) //商户号配置信息
- mchProxy := getMchProxyInitClient(payCode, order_model.SOURCE_XCX)
- outTradeNo = fmt.Sprintf("%s_%d", outTradeNo, time.Now().Unix())
- u := uuid.NewV4().String()
- us := strings.Split(u, "-")
- nonce_str := strings.Join(us, "")
- req := map[string]string{
- "mch_id": mechantInfo.MchId,
- "appid": appId,
- "nonce_str": nonce_str, //fmt.Sprintf("%d", time.Now().Unix()),
- "body": body,
- "out_trade_no": outTradeNo,
- "total_fee": fmt.Sprintf("%d", totalPrice), //分为单位
- "spbill_create_ip": remoteIp,
- "notify_url": notifyUrl,
- // "product_id": outTradeNo,
- "trade_type": "JSAPI",
- // "limit_pay": "no_credit",
- //"用户在商户 appid 下的唯一标识,trade_type 为 JSAPI时,此参数必传,获取方式见表头说明。",
- "openid": openid}
- if limit {
- req["limit_pay"] = "no_credit"
- }
- sign := mch.Sign(req, mechantInfo.ApiKey, nil)
- req["sign"] = sign
- //beego.BeeLogger.Warn("%v", req)
- ret, err := pay.UnifiedOrder(mchProxy, req)
- if err != nil {
- beego.BeeLogger.Error("GetPrepayId err[%s]", err)
- //fmt.Println(err)
- return ""
- }
- return ret["prepay_id"]
- }
- // 订单退款接口
- func GetRefundDataPay(outTradeNo, outRefundNo string, totalPrice int64, transactionId, remark, payCode string) (ret map[string]string) {
- outTradeNo = fmt.Sprintf("%s_%d", outTradeNo, time.Now().Unix())
- appId := beego.AppConfig.String("WxFohowXcxAppId") //获取小程序渠道appId
- mechantInfo := GetMechantInfo(payCode) //商户号配置信息
- mchTLSProxy := getMchTLSProxyInitClient(payCode)
- u := uuid.NewV4().String()
- us := strings.Split(u, "-")
- nonce_str := strings.Join(us, "")
- req := map[string]string{
- "mch_id": mechantInfo.MchId,
- "appid": appId,
- "nonce_str": nonce_str, //fmt.Sprintf("%d", time.Now().Unix()),
- "transaction_id": transactionId,
- //"out_trade_no": outTradeNo,
- "out_refund_no": outRefundNo,
- "total_fee": fmt.Sprintf("%d", totalPrice), //分为单位,订单总金额
- "refund_fee": fmt.Sprintf("%d", totalPrice), //分为单位,退款总金额
- //"notify_url": notifyUrl,
- "refund_desc": remark,
- }
- sign := mch.Sign(req, mechantInfo.ApiKey, nil)
- req["sign"] = sign
- beego.BeeLogger.Warn("%v", req)
- ret, err := pay.Refund(mchTLSProxy, req)
- if err != nil {
- beego.BeeLogger.Error("getGetRefundDataPay err[%s]", err)
- //fmt.Println(err)
- return nil
- }
- return ret
- }
- func getTradeNo(prefix string) string {
- n := time.Now().Format("20060102")
- u := uuid.NewV4().String()
- c := strings.Split(u, "-")
- s := strings.ToUpper(fmt.Sprintf("%s%s%s", prefix, n, c[0]))
- return s
- }
- // 企业付款
- //
- // NO_CHECK:不校验真实姓名
- // FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账)
- // OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
- // 如果check_name设置为FORCE_CHECK或OPTION_CHECK,则必填用户真实姓名
- func Transfers(openid string, rmb int64, tradeNo, check, realName, payCode, desc string) map[string]string {
- // tradNo := getTradeNo("EP")
- appId := beego.AppConfig.String("WxFohowXcxAppId") //获取小程序渠道appId
- mechantInfo := GetMechantInfo(payCode) //商户号配置信息
- mchTLSProxy := getMchTLSProxyInitClient(payCode)
- req := map[string]string{
- "mch_appid": appId,
- "mchid": mechantInfo.MchId,
- "nonce_str": fmt.Sprintf("%d", time.Now().Unix()),
- "partner_trade_no": tradeNo,
- "openid": openid,
- "check_name": check,
- "re_user_name": realName,
- "amount": fmt.Sprintf("%d", rmb),
- "desc": desc,
- "spbill_create_ip": "8.129.187.89"}
- sign := mch.Sign(req, mechantInfo.ApiKey, nil)
- req["sign"] = sign
- ret, _ := promotion.Transfers(mchTLSProxy, req)
- //替换为原fohow微信支付
- //ret, _ := promotion.Transfers(takeMchTLSProxy, req)
- // if err != nil {
- // beego.BeeLogger.Error("Transfers(%s) err[%s],ret[%v]", openid, err, ret)
- // // return nil
- // }
- if ret["return_code"] != "SUCCESS" {
- return nil
- }
- return ret
- // if ret["result_code"] == "SUCCESS" {
- // return ret
- // } else {
- // beego.BeeLogger.Error("Transfers(%s) err[%s],ret[%v]", openid, err, ret)
- // return nil
- // }
- }
- // 校验支付结果回调参数
- func VerifyWxPayResult(payResult *PayResult, payCode string) bool {
- mechantInfo := GetMechantInfo(payCode) //商户号配置信息
- sign := payResult.Sign
- params, err := XmlStructToMap(payResult)
- if err != nil {
- beego.BeeLogger.Error("transfer xmlstruct to map err=[%s]", err)
- return false
- }
- // beego.BeeLogger.Warn("*****params: %v", params)
- // beego.BeeLogger.Warn("*****sign: %s", sign)
- mchSign := mch.Sign(params, mechantInfo.ApiKey, nil)
- // beego.BeeLogger.Warn("*****mch sign: %s", mchSign)
- return sign == mchSign
- }
- // 解析支付结果参数
- func ParsePayResult(data []byte) (*PayResult, error) {
- tt := data
- beego.BeeLogger.Warn("wx_mp.ParsePayResult() %s", string(tt[:]))
- var result PayResult
- err := xml.Unmarshal(data, &result)
- if err != nil {
- return nil, err
- }
- return &result, nil
- }
- // 校验支付结果回调参数
- func VerifyGzhPayResult(payResult *PayResult, payCode string) bool {
- mechantInfo := GetMechantInfo(payCode) //商户号配置信息
- sign := payResult.Sign
- params, err := XmlStructToMap(payResult)
- if err != nil {
- beego.BeeLogger.Error("transfer xmlstruct to map err=[%s]", err)
- return false
- }
- // beego.BeeLogger.Warn("*****params: %v", params)
- // beego.BeeLogger.Warn("*****sign: %s", sign)
- mchSign := mch.Sign(params, mechantInfo.ApiKey, nil)
- // beego.BeeLogger.Warn("*****mch sign: %s", mchSign)
- return sign == mchSign
- }
- // 校验支付结果回调参数
- func VerifyPayResult(payResult *PayResult, payCode string) bool {
- mechantInfo := GetMechantInfo(payCode) //商户号配置信息
- sign := payResult.Sign
- params, err := XmlStructToMap(payResult)
- if err != nil {
- beego.BeeLogger.Error("transfer xmlstruct to map err=[%s]", err)
- return false
- }
- //beego.BeeLogger.Warn("*****params: %v", params)
- //beego.BeeLogger.Warn("*****sign: %s", sign)
- mchSign := mch.Sign(params, mechantInfo.ApiKey, nil)
- // beego.BeeLogger.Warn("*****mch sign: %s", mchSign)
- return sign == mchSign
- }
- // map换为xml字符串
- func MapToXmlString(in map[string]string) string {
- xml := "<xml>"
- for k, v := range in {
- xml = xml + fmt.Sprintf("<%s>%s</%s>", k, v, k)
- }
- xml = xml + "</xml>"
- return xml
- }
- // 将xml结构体换为map
- func XmlStructToMap(in interface{}) (map[string]string, error) {
- out := make(map[string]string, 0)
- v := reflect.ValueOf(in)
- if v.Kind() == reflect.Ptr {
- v = v.Elem()
- }
- if v.Kind() != reflect.Struct {
- return nil, fmt.Errorf("ToMap only accepts structs; got %T", v)
- }
- typ := v.Type()
- for i := 0; i < v.NumField(); i++ {
- fi := typ.Field(i)
- if tagv := fi.Tag.Get("xml"); tagv != "" && tagv != "xml" && tagv != "sign" {
- out[tagv] = v.Field(i).String()
- }
- }
- return out, nil
- }
|