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 := "" for k, v := range in { xml = xml + fmt.Sprintf("<%s>%s", k, v, k) } 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 }