xuzhiheng 3 年之前
父節點
當前提交
a851c09814

+ 3 - 1
README.md

@@ -1 +1,3 @@
-支付中心
+支付中心
+
+$ goctl rpc proto -src payCenter.proto -dir .

+ 10 - 0
go.mod

@@ -0,0 +1,10 @@
+module bp.jydev.jianyu360.cn/BaseService/payCenter
+
+go 1.17
+
+require (
+	github.com/golang/protobuf v1.5.2
+	github.com/zeromicro/go-zero v1.3.3
+	google.golang.org/grpc v1.46.0
+	google.golang.org/protobuf v1.28.0
+)

+ 312 - 0
pay/aliPay.go

@@ -0,0 +1,312 @@
+package pay
+
+import (
+	"config"
+	"crypto"
+	"crypto/rsa"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"net/url"
+	qutil "qfw/util"
+	"strings"
+	"time"
+)
+
+type aliPayStruct struct {
+	Appid                     string
+	PrivateKey                *rsa.PrivateKey //请求接口秘钥
+	AliPublicKey              *rsa.PublicKey  //支付完成回调,校验使用(取支付宝后台)
+	CallBackUrl               string          //支付完成回调地址
+	Requseturl                string          //接口请求地址
+	Subject_sjdc              string          //支付小标题
+	Subject_sjbg              string          //支付小标题
+	Subject_subvip            string          //支付小标题
+	Subject_course            string          //支付小标题
+	Subject_courseOnline      string          //支付小标题
+	Subject_entniche          string          //支付小标题
+	Subject_member            string          //支付小标题
+	Subject_aiforecastpack    string          //支付小标题
+	Subject_bidfile           string          //支付小标题
+	Subject_integral          string          //剑鱼币支付小标题
+	Subject_dataPack          string          //数据包支付小标题
+	Subject_areaPack          string          //省份流量包支付小标题
+	Subject_filePack          string          //附件下载包支付小标题
+	Subject_buyerPortraitPack string          //采购单位画像包支付小标题
+}
+
+var Alipay *aliPayStruct
+
+func init() {
+	//加载本地私钥
+	log.Println(qutil.ObjToString(config.AliPayConf["privateKey"]))
+	privateKey, err := parsePrivateKey(qutil.ObjToString(config.AliPayConf["privateKey"]))
+	if err != nil {
+		panic(err)
+	}
+	//加载支付宝后台公钥
+	aliPublicKey, err := parseAliPayPublicKey(qutil.ObjToString(config.AliPayConf["callbackPublicKey"]))
+	if err != nil {
+		panic(err)
+	}
+	Alipay = &aliPayStruct{
+		Appid:                     qutil.ObjToString(config.AliPayConf["appid"]),
+		PrivateKey:                privateKey,
+		AliPublicKey:              aliPublicKey,
+		CallBackUrl:               qutil.ObjToString(config.Config.WebDomain) + "/jypay/callback/aliPay",
+		Requseturl:                qutil.ObjToString(config.AliPayConf["reqUrl"]),
+		Subject_sjdc:              qutil.ObjToString(config.AliPayConf["Subject_sjdc"]),
+		Subject_sjbg:              qutil.ObjToString(config.AliPayConf["Subject_sjbg"]),
+		Subject_subvip:            qutil.ObjToString(config.AliPayConf["Subject_subvip"]),
+		Subject_course:            qutil.ObjToString(config.AliPayConf["Subject_course"]),
+		Subject_entniche:          qutil.ObjToString(config.AliPayConf["Subject_entniche"]),
+		Subject_courseOnline:      qutil.ObjToString(config.AliPayConf["Subject_courseOnline"]),
+		Subject_member:            qutil.ObjToString(config.AliPayConf["Subject_member"]),
+		Subject_aiforecastpack:    qutil.ObjToString(config.AliPayConf["Subject_aiforecastpack"]),
+		Subject_bidfile:           qutil.ObjToString(config.AliPayConf["Subject_bidfile"]),
+		Subject_integral:          qutil.ObjToString(config.AliPayConf["Subject_integral"]),
+		Subject_dataPack:          qutil.ObjToString(config.AliPayConf["Subject_dataPack"]),
+		Subject_areaPack:          qutil.ObjToString(config.AliPayConf["Subject_areaPack"]),
+		Subject_filePack:          qutil.ObjToString(config.AliPayConf["Subject_filePack"]),
+		Subject_buyerPortraitPack: qutil.ObjToString(config.AliPayConf["Subject_buyerPortraitPack"]),
+	}
+}
+
+const (
+	//支付宝接口
+	Method_Type_Create_Pc  = "alipay.trade.page.pay" //支付宝获取pc端支付接口
+	Method_Type_Create_App = "alipay.trade.app.pay"  //支付宝获取App端支付接口
+	Method_Type_Close      = "alipay.trade.close"    //支付宝订单关闭接口
+
+	//订单标识
+	ALI_DATAEXPORT_NATIVE = "z" //支付宝数据导出-扫码支付
+	ALI_DATAEXPORT_APP    = "Z" //支付宝数据导出-app支付
+	ALI_DATAREPORT_APP    = "X" //支付宝数据报告-app支付
+	ALI_DATAREPORT_NATIVE = "x" //支付宝数据报告-扫码支付
+
+	ALI_SUBVIP_NATIVE         = "y"  //支付宝vip订阅-扫码支付
+	ALI_SUBVIP_APP            = "Y"  //支付宝数vip订阅--扫码支付
+	ALI_COURSE_NATIVE         = "v"  //剑鱼招投标课程-扫码支付
+	ALI_COURSE_APP            = "V"  //剑鱼招投标课程-app支付
+	ALI_ENTNICHE_APP          = "U"  //支付宝企业商机管理--app支付
+	ALI_ENTNICHE_NATIVE       = "u"  //支付宝企业商机管理--pc支付
+	ALI_COURSEONLINE_NATIVE   = "t"  //剑鱼线上课程-扫码支付
+	ALI_COURSEONLINE_APP      = "T"  //剑鱼线上课程-app支付
+	ALI_MEMBER_NATIVE         = "s"  //剑鱼大会员-扫码支付
+	ALI_MEMBER_APP            = "S"  //剑鱼大会员-扫码支付
+	ALI_AIFORECASTPACK_NATIVE = "r"  //剑鱼大会员-ai中标预测包-扫码支付
+	ALI_AIFORECASTPACK_APP    = "R"  //剑鱼大会员-ai中标预测包-扫码支付
+	ALI_BIDFILE_NATIVE        = "q"  //招标文件解读扫码支付
+	ALI_BIDFILE_APP           = "Q"  //招标文件解读app支付
+	ALI_INTEGRAL_NATIVE       = "aa" //剑鱼币扫码支付
+	ALI_INTEGRAL_APP          = "AB" //剑鱼币app支付
+
+	/*新命名规范
+	  Ali 「app支付」 ali 「pc扫码支付」
+	  P 「产品标识」 pack 数据包
+	*/
+	ALI_DATAPACK_APP    = "ALIP" //数据包-app支付
+	ALI_DATAPACK_NATIVE = "alip" //数据包-pc支付
+
+	ALI_AREAPACK_APP    = "ALIA" //省份流量包-app支付
+	ALI_AREAPACK_NATIVE = "alia" //省份流量包-pc支付
+
+	ALI_FILEPACK_APP    = "ALIF" //附件下载包包 -app支付
+	ALI_FILEPACK_NATIVE = "alif" //附件下载包 -pc支付
+
+	ALI_BUYERPORTRAITPACK_APP    = "ALIB" //采购单位画像包 -app支付
+	ALI_BUYERPORTRAITPACK_NATIVE = "alib" //采购单位画像包 -pc支付
+)
+
+func (w *aliPayStruct) GetTradeno(tradenoSign string) (string, string) {
+	tradeno, subject := "", ""
+	if tradenoSign == ALI_DATAEXPORT_NATIVE || tradenoSign == ALI_DATAEXPORT_APP {
+		subject = w.Subject_sjdc
+	} else if tradenoSign == ALI_DATAREPORT_APP || tradenoSign == ALI_DATAREPORT_NATIVE {
+		subject = w.Subject_sjbg
+	} else if tradenoSign == ALI_SUBVIP_APP || tradenoSign == ALI_SUBVIP_NATIVE {
+		subject = w.Subject_subvip
+	} else if tradenoSign == ALI_COURSE_NATIVE || tradenoSign == ALI_COURSE_APP {
+		subject = w.Subject_course
+	} else if tradenoSign == ALI_ENTNICHE_APP || tradenoSign == ALI_ENTNICHE_NATIVE {
+		subject = w.Subject_entniche
+	} else if tradenoSign == ALI_COURSEONLINE_NATIVE || tradenoSign == ALI_COURSEONLINE_APP {
+		subject = w.Subject_courseOnline
+	} else if tradenoSign == ALI_MEMBER_NATIVE || tradenoSign == ALI_MEMBER_APP {
+		subject = w.Subject_member
+	} else if tradenoSign == ALI_AIFORECASTPACK_NATIVE || tradenoSign == ALI_AIFORECASTPACK_APP {
+		subject = w.Subject_aiforecastpack
+	} else if tradenoSign == ALI_BIDFILE_APP || tradenoSign == ALI_BIDFILE_NATIVE {
+		subject = w.Subject_bidfile
+	} else if tradenoSign == ALI_INTEGRAL_APP || tradenoSign == ALI_INTEGRAL_NATIVE {
+		subject = w.Subject_integral
+	} else if tradenoSign == ALI_DATAPACK_NATIVE || tradenoSign == ALI_DATAPACK_APP {
+		subject = w.Subject_dataPack
+	} else if tradenoSign == ALI_AREAPACK_APP || tradenoSign == ALI_AREAPACK_NATIVE {
+		subject = w.Subject_areaPack
+	} else if tradenoSign == ALI_FILEPACK_APP || tradenoSign == ALI_FILEPACK_NATIVE {
+		subject = w.Subject_filePack
+	} else if tradenoSign == ALI_BUYERPORTRAITPACK_APP || tradenoSign == ALI_BUYERPORTRAITPACK_NATIVE {
+		subject = w.Subject_buyerPortraitPack
+	}
+	lenRandom := 6 - len([]rune(tradenoSign)) //控制长度
+	tradeno = fmt.Sprintf("%s_%d%s%s", tradenoSign, time.Now().UnixNano(), qutil.GetRandom(lenRandom), qutil.GetLetterRandom(6))
+	tradeno = processTradeno(tradeno)
+	return tradeno, subject
+}
+
+//获取网页支付二维码地址
+func (a *aliPayStruct) GetOrderPayParam(order_money float64, tradenoSign string) (string, string, error) {
+	//测试环境,1分钱
+	if config.Config.AllPayMoney > 0 {
+		order_money = float64(config.Config.AllPayMoney)
+	}
+	//支付标题
+	tradeno, subject := a.GetTradeno(tradenoSign)
+	var bizByte []byte
+	methodStr := ""
+	if strings.ToUpper(tradenoSign) == tradenoSign { //app支付
+		bizContent := struct {
+			Subject        string  `json:"subject"`
+			OutTradeNo     string  `json:"out_trade_no"`
+			TotalAmount    float64 `json:"total_amount"`
+			ProductCode    string  `json:"product_code"`
+			TimeoutExpress string  `json:"timeout_express"`
+		}{
+			Subject:        subject,
+			OutTradeNo:     tradeno,
+			TotalAmount:    order_money / 100,
+			ProductCode:    "QUICK_MSECURITY_PAY",
+			TimeoutExpress: "2h",
+		}
+		bizByte, _ = json.Marshal(bizContent)
+		methodStr = Method_Type_Create_App
+	} else { //扫码支付
+		bizContent := struct {
+			Subject        string  `json:"subject"`
+			OutTradeNo     string  `json:"out_trade_no"`
+			TotalAmount    float64 `json:"total_amount"`
+			ProductCode    string  `json:"product_code"`
+			Qr_pay_mode    string  `json:"qr_pay_mode"`
+			Qrcode_width   int     `json:"qrcode_width"`
+			TimeoutExpress string  `json:"timeout_express"`
+		}{
+			Subject:        subject,
+			OutTradeNo:     tradeno,
+			TotalAmount:    order_money / 100,
+			ProductCode:    "FAST_INSTANT_TRADE_PAY",
+			Qr_pay_mode:    "4",
+			Qrcode_width:   250,
+			TimeoutExpress: "2h",
+		}
+		bizByte, _ = json.Marshal(bizContent)
+		methodStr = Method_Type_Create_Pc
+	}
+
+	var data = url.Values{}
+	data.Add("app_id", Alipay.Appid)
+	data.Add("method", methodStr)
+	data.Add("notify_url", a.CallBackUrl) //支付完成回调地址
+	data.Add("format", "json")
+	data.Add("charset", "UTF-8")
+	data.Add("sign_type", "RSA2")
+	data.Add("timestamp", time.Now().Format("2006-01-02 15:04:05"))
+	data.Add("version", "1.0")
+	data.Add("biz_content", string(bizByte))
+
+	signContentBytes, _ := url.QueryUnescape(data.Encode())
+	signature, err := a.getSign([]byte(signContentBytes))
+	if err != nil {
+		return "", "", err
+	}
+	data.Add("sign", signature)
+	if strings.ToUpper(tradenoSign) == tradenoSign { //app支付
+		return data.Encode(), tradeno, nil
+	} else {
+		return a.Requseturl + "?" + data.Encode(), tradeno, nil
+	}
+}
+
+//校验回调sign
+func (a *aliPayStruct) CheckCallBackSign(param, sign string) bool {
+	newHash := sha256.New()
+	newHash.Write([]byte(param))
+	hashed := newHash.Sum(nil)
+	cryptoHash := crypto.SHA256
+
+	signDecoded, err := base64.StdEncoding.DecodeString(sign)
+	if err != nil {
+		fmt.Println("checkCallBackSign DecodeString err:", err)
+		return false
+	}
+	if err := rsa.VerifyPKCS1v15(Alipay.AliPublicKey, cryptoHash, hashed, signDecoded); err != nil {
+		if err != rsa.ErrVerification {
+			log.Println("VerifyPKCS1v15 err:", err)
+		}
+		return false
+	}
+	return true
+}
+
+//支付宝支付统一HTTP调用接口
+func (a *aliPayStruct) DoRequest(content, tradeno, methodType string) (*map[string]interface{}, error) {
+	var data = url.Values{}
+	data.Add("app_id", Alipay.Appid)
+	data.Add("method", methodType)
+	data.Add("format", "json")
+	data.Add("charset", "UTF-8")
+	data.Add("sign_type", "RSA2")
+	data.Add("timestamp", time.Now().Format("2006-01-02 15:04:05"))
+	data.Add("version", "1.0")
+	data.Add("biz_content", content)
+	signContentBytes, _ := url.QueryUnescape(data.Encode())
+	signature, err := a.getSign([]byte(signContentBytes))
+	if err != nil {
+		return nil, err
+	}
+	data.Add("sign", signature)
+
+	url := a.Requseturl + "?" + data.Encode()
+	client := http.Client{Jar: nil}
+	req, _ := http.NewRequest("POST", url, nil)
+	res, err := client.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	bArr, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		return nil, err
+	}
+	m := map[string]interface{}{}
+	err = json.Unmarshal(bArr, &m)
+	return &m, nil
+}
+
+//支付宝关闭订单(生成的订单二维码,未扫码无法关闭订单)
+func (a *aliPayStruct) CloseOrder(tradeno string) bool {
+	toClose := struct {
+		OutTradeNo string `json:"out_trade_no"`
+	}{
+		OutTradeNo: tradeno,
+	}
+	bizbyte, _ := json.Marshal(toClose)
+	resMap, err := Alipay.DoRequest(string(bizbyte), tradeno, Method_Type_Close)
+	if err != nil {
+		log.Printf("%s 支付宝关闭订单出错 %v\n", tradeno, err)
+	}
+	valus := *qutil.ObjToMap((*resMap)["alipay_trade_close_response"])
+	log.Printf("%s 订单关闭相应参数  %+v", tradeno, valus)
+	if valus["sub_code"] == "ACQ.TRADE_STATUS_ERROR" || valus["sub_msg"] == "REASON_ILLEGAL_STATUS" {
+		return false
+	}
+	/*if valus["msg"] == "Success" {
+	  	return true
+	  } else if valus["sub_code"] == "ACQ.INVALID_PARAMETER" || valus["sub_code"] == "ACQ.TRADE_STATUS_ERROR" || valus["sub_code"] == "ACQ.SYSTEM_ERROR" { //参数异常  交易状态不合法  系统异常
+	  	return false
+	  }*/
+	return true
+}

+ 396 - 0
pay/util.go

@@ -0,0 +1,396 @@
+package pay
+
+import (
+	"config"
+	"crypto"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha256"
+	"crypto/x509"
+	"encoding/base64"
+	"encoding/pem"
+	"errors"
+	"fmt"
+	"hash"
+	"log"
+	qutil "qfw/util"
+	"strings"
+	"sync"
+	"time"
+	"util"
+)
+
+/*
+数据库字段   | 页面dotype
+-----------------------
+vip订阅     | subvip
+企业商机管理 | entniche
+历史数据	   | dataexport
+招投标课程   | course
+数据报告     | datareport
+中标必听课     | onlineCourse
+大会员      |  member
+AI中标预测包 |  aiForecastPack
+招标文件解读 |  bidfile
+*/
+
+var (
+	vipMap = map[string]string{
+		"wx_js":   WX_SUBVIP_JSAPI,
+		"wx_app":  WX_SUBVIP_APP,
+		"wx_pc":   WX_SUBVIP_NATIVE,
+		"ali_app": ALI_SUBVIP_APP,
+		"ali_pc":  ALI_SUBVIP_NATIVE,
+	}
+	entnicheMap = map[string]string{
+		"wx_app":  WX_ENTNICHE_APP,
+		"wx_pc":   WX_ENTNICHE_NATIVE,
+		"ali_app": ALI_ENTNICHE_APP,
+		"ali_pc":  ALI_ENTNICHE_NATIVE,
+		//"wx_js":   "",
+	}
+	dataexportMap = map[string]string{
+		"wx_js":   WX_DATAEXPORT_JSAPI,
+		"wx_app":  WX_DATAEXPORT_APP,
+		"wx_pc":   WX_DATAEXPORT_NATIVE,
+		"ali_app": ALI_DATAEXPORT_APP,
+		"ali_pc":  ALI_DATAEXPORT_NATIVE,
+	}
+	courseMap = map[string]string{
+		"wx_js":   WX_COURSE_JSAPI,
+		"wx_app":  WX_COURSE_APP,
+		"wx_pc":   WX_COURSE_NATIVE,
+		"ali_app": ALI_COURSE_APP,
+		"ali_pc":  ALI_COURSE_NATIVE,
+	}
+	datareportMap = map[string]string{
+		"wx_js":   WX_DATAREPORT_JSAPI,
+		"wx_app":  WX_DATAREPORT_APP,
+		"wx_pc":   WX_DATAREPORT_NATIVE,
+		"ali_app": ALI_DATAREPORT_APP,
+		"ali_pc":  ALI_DATAREPORT_NATIVE,
+	}
+	onlineCourseMap = map[string]string{
+		"wx_js":   WX_COURSEONLINE_JSAPI,
+		"wx_app":  WX_COURSEONLINE_APP,
+		"wx_pc":   WX_COURSEONLINE_NATIVE,
+		"ali_app": ALI_COURSEONLINE_APP,
+		"ali_pc":  ALI_COURSEONLINE_NATIVE,
+	}
+	//大会员
+	memberMap = map[string]string{
+		"wx_js":   WX_MEMBER_JSAPI,
+		"wx_app":  WX_MEMBER_APP,
+		"wx_pc":   WX_MEMBER_NATIVE,
+		"ali_app": ALI_MEMBER_APP,
+		"ali_pc":  ALI_MEMBER_NATIVE,
+	}
+	//AI中标预测包
+	aiForecastPackMap = map[string]string{
+		"wx_js":   WX_AIFORECASTPACK_JSAPI,
+		"wx_app":  WX_AIFORECASTPACK_APP,
+		"wx_pc":   WX_AIFORECASTPACK_NATIVE,
+		"ali_app": ALI_AIFORECASTPACK_APP,
+		"ali_pc":  ALI_AIFORECASTPACK_NATIVE,
+	}
+	bifileMap = map[string]string{
+		"wx_js":   WX_BIDFILE_JSAPI,
+		"wx_app":  WX_BIDFILE_APP,
+		"wx_pc":   WX_BIDFILE_NATIVE,
+		"ali_app": ALI_BIDFILE_APP,
+		"ali_pc":  ALI_BIDFILE_NATIVE,
+	}
+	//剑鱼币
+	integralMap = map[string]string{
+		"wx_js":   WX_INTEGRAL_JSAPI,
+		"wx_app":  WX_INTEGRAL_APP,
+		"wx_pc":   WX_INTEGRAL_NATIVE,
+		"ali_app": ALI_INTEGRAL_APP,
+		"ali_pc":  ALI_INTEGRAL_NATIVE,
+	}
+	//数据流量包
+	dataPackMap = map[string]string{
+		"wx_js":   WX_DATAPACK_JSAIP,
+		"wx_app":  WX_DATAPACK_APP,
+		"wx_pc":   WX_DATAPACK_NATIVE,
+		"ali_app": ALI_DATAPACK_APP,
+		"ali_pc":  ALI_DATAPACK_NATIVE,
+	}
+	//省份订阅包
+	areaPackMap = map[string]string{
+		"wx_js":   WX_AREAPACK_JSAPI,
+		"wx_app":  WX_AREAPACK_APP,
+		"wx_pc":   WX_AREAPACK_NATIVE,
+		"ali_app": ALI_AREAPACK_APP,
+		"ali_pc":  ALI_AREAPACK_NATIVE,
+	}
+	//附件下载包
+	filePackMap = map[string]string{
+		"wx_js":   WX_FILEPACK_JSAPI,
+		"wx_app":  WX_FILEPACK_APP,
+		"wx_pc":   WX_FILEPACK_NATIVE,
+		"ali_app": ALI_FILEPACK_APP,
+		"ali_pc":  ALI_FILEPACK_NATIVE,
+	}
+	//采购单位画像包
+	buyerPortraitPackMap = map[string]string{
+		"wx_js":   WX_BUYERPORTRAITPACK_JSAPI,
+		"wx_app":  WX_BUYERPORTRAITPACK_APP,
+		"wx_pc":   WX_BUYERPORTRAITPACK_NATIVE,
+		"ali_app": ALI_BUYERPORTRAITPACK_APP,
+		"ali_pc":  ALI_BUYERPORTRAITPACK_NATIVE,
+	}
+
+	PayWayAndSign = map[string]map[string]string{
+		"subvip":            vipMap,
+		"entniche":          entnicheMap,
+		"dataexport":        dataexportMap,
+		"course":            courseMap,
+		"datareport":        datareportMap,
+		"onlineCourse":      onlineCourseMap,
+		"member":            memberMap,
+		"aiForecastPack":    aiForecastPackMap,
+		"bidfile":           bifileMap,
+		"integral":          integralMap,
+		"areaPack":          areaPackMap,
+		"filePack":          filePackMap,
+		"buyerPortraitPack": buyerPortraitPackMap,
+		"VIP订阅":             vipMap,
+		"企业商机管理":            entnicheMap,
+		"历史数据":              dataexportMap,
+		"招投标课程":             courseMap,
+		"数据报告":              datareportMap,
+		"中标必听课":             onlineCourseMap,
+		"大会员":               memberMap,
+		"大会员-AI中标预测包":       aiForecastPackMap,
+		"招标文件解读":            bifileMap,
+		"大会员-招标文件解读":        bifileMap,
+		"剑鱼币":               integralMap,
+		"数据流量包":             dataPackMap,
+		"省份订阅包":             areaPackMap,
+		"附件下载包":             filePackMap,
+		"采购单位画像包":           buyerPortraitPackMap,
+	}
+	varOrderCode *orderCode
+
+	ProductType = map[string]string{
+		"subvip":         "VIP订阅",
+		"entniche":       "企业商机管理",
+		"dataexport":     "历史数据",
+		"datareport":     "数据报告",
+		"onlineCourse":   "中标必听课",
+		"member":         "大会员",
+		"aiForecastPack": "大会员-AI中标预测包",
+		"integral":       "剑鱼币",
+		"dataPack":       "数据流量包",
+		"areaPack":       "省份订阅包",
+		"filePack":       "附件下载包",
+	}
+)
+
+type orderCode struct {
+	pool chan string
+	all  *sync.Map
+	lock *sync.Mutex
+}
+
+func (o *orderCode) gc() {
+	varOrderCode.all.Range(func(key, value interface{}) bool {
+		if time.Now().Day() != value.(int) {
+			varOrderCode.all.Delete(key)
+		}
+		return true
+	})
+	time.AfterFunc(24*time.Hour, o.gc)
+}
+func init() {
+	varOrderCode = &orderCode{
+		pool: make(chan string, 20),
+		all:  &sync.Map{},
+		lock: &sync.Mutex{},
+	}
+	varOrderCode.gc()
+	for i := 0; i < 10; i++ {
+		go func() {
+			varOrderCode.lock.Lock()
+			defer varOrderCode.lock.Unlock()
+			for {
+				o := fmt.Sprintf("%d%s", time.Now().Unix()+900000000, qutil.GetRandom(2))
+				if _, ok := varOrderCode.all.Load(o); ok {
+					continue
+				}
+				varOrderCode.all.Store(o, time.Now().Day())
+				varOrderCode.pool <- o
+			}
+		}()
+	}
+}
+
+/*
+公共创建订单
+price 价格
+productSign 产品种类标识
+ip 用户ip(仅微信支付需要)
+openid 用户openid(仅微信支付需要)
+payWay 支付方式
+*/
+func CreateOrderPay(price int, productSign, ip, openid, payWay string) (tradeno, prepayid, payParam string, err error) {
+	switch payWay {
+	case "wx_js", "wx_app", "wx_pc": //微信公众号支付,微信app支付,微信扫码支付
+		var ret *map[string]string
+		tradeno, ret = WxStruct.CreatePrepayOrder(productSign, ip, openid, "", price)
+		log.Println(tradeno, ret)
+		if ret == nil {
+			err = errors.New("创建异常")
+			return
+		}
+		if (*ret)["status"] != "1" {
+			errMsg := "创建微信订单出错"
+			if (*ret)["errcodedes"] != "" {
+				errMsg = (*ret)["errcodedes"]
+			} else if (*ret)["errcode"] != "" {
+				errMsg = (*ret)["errcode"]
+			}
+			err = errors.New(errMsg)
+			return
+		}
+		prepayid = qutil.ObjToString((*ret)["prepayid"])
+		if payWay == "wx_js" {
+			payParam = WxStruct.GetWxjsPaySign(prepayid)
+		} else if payWay == "wx_app" {
+			payParam = WxStruct.GetAppWxPayStr(prepayid)
+		} else if payWay == "wx_pc" {
+			payParam = (*ret)["codeurl"]
+		}
+	case "ali_app", "ali_pc": //支付宝app支付,支付宝扫码支付
+		payParam, tradeno, err = Alipay.GetOrderPayParam(qutil.Float64All(price), productSign)
+		if err != nil {
+			err = errors.New("创建支付宝订单出错")
+			return
+		}
+	}
+	return
+}
+
+//关闭订单
+func CloseOrderByOrderCode(ordercode string) error {
+	res := util.Mysql.FindOne("dataexport_order", map[string]interface{}{"order_code": ordercode}, "out_trade_no,pay_way", "")
+	if res == nil || len(*res) == 0 {
+		return errors.New("关闭订单出错,未找到订单" + ordercode)
+	}
+	tradeno := qutil.ObjToString((*res)["out_trade_no"])
+	if tradeno == "" {
+		return errors.New("关闭订单出错,tradeno异常" + ordercode)
+	}
+	payWay := qutil.ObjToString((*res)["pay_way"])
+	if payWay == "" {
+		return errors.New("关闭订单出错,pay_way异常" + ordercode)
+	}
+	if !CloseOrder(tradeno, payWay) {
+		return errors.New(fmt.Sprintf("%s关闭订单异常%s", ordercode, payWay))
+	}
+	return nil
+}
+
+func CloseOrder(tradeno, payWay string) bool {
+	if payWay == "transferAccounts" {
+		return true
+	}
+	if strings.HasPrefix(payWay, "wx") {
+		return WxStruct.CloseOrder(tradeno, payWay)
+
+	} else if strings.HasPrefix(payWay, "ali") {
+		return Alipay.CloseOrder(tradeno)
+	}
+	return false
+}
+
+//创建订单号
+func GetOrderCode(id ...string) string {
+	//2020-08-01之前的规则 fmt.Sprintf("%s%s", time.Now().Format("150405"), qutil.GetRandom(6))
+	return <-varOrderCode.pool
+}
+
+//关闭订单
+func CloseDataExportOrder(payWay, tradeno, prepayTime string) (status bool) {
+	//如果订单超时 则不用关闭订单
+	t, err := time.ParseInLocation(qutil.Date_Full_Layout, prepayTime, time.Local)
+	if err == nil {
+		if t.Add(time.Hour * 2).Before(time.Now()) {
+			return true
+		}
+	}
+	//没有选择支付方式或者是测试数据
+	if payWay == "" || payWay == "测试使用" || payWay == "transferAccounts" || tradeno == "" {
+		return true
+	}
+	log.Printf("%s取消订单,订单号%s\n", payWay, tradeno)
+	if strings.HasPrefix(payWay, "ali_") {
+		status = Alipay.CloseOrder(tradeno)
+	} else if strings.HasPrefix(payWay, "wx_") {
+		status = WxStruct.CloseOrder(tradeno, payWay)
+	}
+	if !status {
+		log.Printf("%s订单关闭失败:%s\n", payWay, tradeno)
+	}
+	return status
+}
+
+//加载秘钥
+func parsePrivateKey(key string) (*rsa.PrivateKey, error) {
+	block, _ := pem.Decode([]byte(key))
+	if block == nil {
+		return nil, errors.New("私钥格式不正确")
+	}
+	if strings.ToUpper(block.Type) != "RSA PRIVATE KEY" {
+		return nil, errors.New("私钥类型不正确" + block.Type)
+	}
+	rsaPrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
+	if err != nil {
+		return nil, err
+	}
+	return rsaPrivateKey.(*rsa.PrivateKey), nil
+}
+
+//加载支付宝后台公钥
+func parseAliPayPublicKey(key string) (*rsa.PublicKey, error) {
+	publicKeyDecoded, err := base64.StdEncoding.DecodeString(key)
+	if err != nil {
+		return nil, err
+	}
+	pub, err := x509.ParsePKIXPublicKey(publicKeyDecoded)
+	if err != nil {
+		return nil, err
+	}
+	if pub, ok := pub.(*rsa.PublicKey); ok {
+		return pub, nil
+	}
+	return nil, errors.New("rsa.PublicKey 断言出错")
+}
+
+//生成加密token
+func (a *aliPayStruct) getSign(data []byte) (signature string, err error) {
+	var h hash.Hash
+	var hType crypto.Hash
+	h = sha256.New()
+	hType = crypto.SHA256
+	//}
+	h.Write(data)
+	d := h.Sum(nil)
+	bs, err := rsa.SignPKCS1v15(rand.Reader, a.PrivateKey, hType, d)
+
+	if err != nil {
+		return "", err
+	}
+	signature = base64.StdEncoding.EncodeToString(bs)
+	return
+}
+
+//处理商户订单号
+func processTradeno(tradeno string) string {
+	if devTradenoSign, _ := config.PayConf["devTradenoSign"].(string); devTradenoSign != "" {
+		tradeno = devTradenoSign + tradeno
+		tradeno = string([]rune(tradeno)[:len([]rune(tradeno))-len([]rune(devTradenoSign))])
+	}
+	return tradeno
+}

+ 319 - 0
pay/wxPay.go

@@ -0,0 +1,319 @@
+package pay
+
+import (
+	"config"
+	"encoding/json"
+	"encoding/xml"
+	"fmt"
+	"log"
+	"net/rpc"
+	"qfw/util"
+	"time"
+)
+
+const (
+	WX_REWARD_JSAPI          = "a"  //微信打赏
+	WX_DATAEXPORT_NATIVE     = "b"  //数据导出-扫码支付
+	WX_DATAEXPORT_JSAPI      = "c"  //数据导出-微信js支付
+	WX_DATAEXPORT_APP        = "C"  //数据导出-微信app支付
+	WX_DATAREPORT_JSAPI      = "d"  //数据报告-微信js支付
+	WX_DATAREPORT_APP        = "D"  //数据报告-微信app支付
+	WX_SUBVIP_JSAPI          = "e"  //VIP订阅-js支付
+	WX_SUBVIP_APP            = "E"  //VIP订阅-APP支付
+	WX_COURSE_JSAPI          = "f"  //剑鱼招投标课程-js支付
+	WX_COURSE_APP            = "F"  //剑鱼招投标课程-APP支付
+	WX_COURSE_NATIVE         = "g"  //剑鱼招投标课程-扫码支付
+	WX_ENTNICHE_APP          = "H"  //企业商机管理-APP支付
+	WX_COURSEONLINE_JSAPI    = "i"  //课程线上课程-js支付
+	WX_COURSEONLINE_APP      = "I"  //剑鱼线上课程-APP支付
+	WX_COURSEONLINE_NATIVE   = "j"  //剑鱼线上课程-扫码支付
+	WX_DATAREPORT_NATIVE     = "k"  //数据报告-扫码支付
+	WX_SUBVIP_NATIVE         = "l"  //VIP订阅-扫码支付
+	WX_ENTNICHE_NATIVE       = "m"  //商机管理-pc支付
+	WX_MEMBER_JSAPI          = "n"  //大会员-微信js支付
+	WX_MEMBER_APP            = "N"  //大会员-APP支付
+	WX_MEMBER_NATIVE         = "o"  //大会员-PC支付
+	WX_AIFORECASTPACK_JSAPI  = "P"  //大会员-AI中标预测包-微信js支付
+	WX_AIFORECASTPACK_APP    = "p"  //大会员-AI中标预测包-APP支付
+	WX_AIFORECASTPACK_NATIVE = "q"  //大会员-AI中标预测包-PC支付
+	WX_BIDFILE_JSAPI         = "R"  //招标文件解读-微信js支付
+	WX_BIDFILE_APP           = "r"  //招标文件解读-APP支付
+	WX_BIDFILE_NATIVE        = "S"  //招标文件解读-PC支付
+	WX_INTEGRAL_JSAPI        = "AA" //剑鱼币-微信js支付
+	WX_INTEGRAL_APP          = "AB" //剑鱼币-APP支付
+	WX_INTEGRAL_NATIVE       = "AC" //剑鱼币-PC支付
+
+	/*新命名规范
+	  A 「ali阿里支付宝支付」 W 「wx微信支付」
+	  A 「app支付」  J「js支付*仅微信端有」 P「pc扫码支付」
+	  P 「产品标识」 pack 数据包
+	*/
+	WX_DATAPACK_JSAIP  = "WJP" //数据包-微信js支付
+	WX_DATAPACK_APP    = "WAP" //数据包-app支付
+	WX_DATAPACK_NATIVE = "WPP" //数据包-pc支付
+
+	WX_AREAPACK_JSAPI  = "WJA" //省份订阅包 微信js支付
+	WX_AREAPACK_APP    = "WAA" //省份订阅包 app微信支付
+	WX_AREAPACK_NATIVE = "WPA" //省份订阅包 微信pc支付
+
+	WX_FILEPACK_JSAPI  = "WJF" //附件下载包 微信js支付
+	WX_FILEPACK_APP    = "WAF" //附件下载包 app微信支付
+	WX_FILEPACK_NATIVE = "WPF" //附件下载包 微信pc支付
+
+	WX_BUYERPORTRAITPACK_JSAPI  = "WJB" //采购单位画像包 微信js支付
+	WX_BUYERPORTRAITPACK_APP    = "WAB" //采购单位画像包 app微信支付
+	WX_BUYERPORTRAITPACK_NATIVE = "WPB" //采购单位画像包 微信pc支付
+)
+
+var WxStruct *WeixinStruct
+
+type WeixinStruct struct {
+	Appid                 string
+	Mchid                 string
+	Appid_app             string //微信支付和app支付appid不一致
+	Key                   string
+	Dashang_attachmsg     string
+	Dashang_bodymsg       string
+	Dashang_detailmsg     string
+	sjdc_msg              string
+	Sjbg_msg              string
+	Subvip_msg            string
+	Entniche_msg          string
+	Course_msg            string
+	CourseOnline_msg      string
+	Member_msg            string
+	Aiforecastpage_msg    string
+	Bidfile_msg           string
+	OpenidSwitch          *map[string]interface{}
+	Integral_msg          string
+	DataPack_msg          string
+	AreaPack_msg          string
+	FilePack_msg          string
+	BuyerPortraitPack_msg string
+}
+
+func init() {
+	payConfig := config.WxPayConf["pay"].(map[string]interface{})
+
+	WxStruct = &WeixinStruct{
+		Appid:                 util.ObjToString(config.WxPayConf["appid"]),
+		Appid_app:             util.ObjToString(config.WxPayConf["appid_app"]),
+		Mchid:                 util.ObjToString(payConfig["mchid"]),
+		Key:                   util.ObjToString(payConfig["key"]),
+		Dashang_attachmsg:     util.ObjToString(payConfig["attachmsg"]),
+		Dashang_bodymsg:       util.ObjToString(payConfig["bodymsg"]),
+		Dashang_detailmsg:     util.ObjToString(payConfig["detailmsg"]),
+		sjdc_msg:              util.ObjToString(payConfig["sjdc_msg"]),
+		Sjbg_msg:              util.ObjToString(payConfig["sjbg_msg"]),
+		Subvip_msg:            util.ObjToString(payConfig["subvip_msg"]),
+		Entniche_msg:          util.ObjToString(payConfig["entniche_msg"]),
+		Course_msg:            util.ObjToString(payConfig["course_msg"]),
+		CourseOnline_msg:      util.ObjToString(payConfig["courseOnline_msg"]),
+		Member_msg:            util.ObjToString(payConfig["member_msg"]),
+		Aiforecastpage_msg:    util.ObjToString(payConfig["aiforecastpage_msg"]),
+		Bidfile_msg:           util.ObjToString(payConfig["bidfile_msg"]),
+		OpenidSwitch:          util.ObjToMap(config.WxPayConf["openidSwitch"]),
+		Integral_msg:          util.ObjToString(payConfig["integral_msg"]),
+		DataPack_msg:          util.ObjToString(payConfig["dataPack_msg"]),
+		AreaPack_msg:          util.ObjToString(payConfig["areaPack_msg"]),
+		FilePack_msg:          util.ObjToString(payConfig["filePack_msg"]),
+		BuyerPortraitPack_msg: util.ObjToString(payConfig["buyerPortraitPack_msg"]),
+	}
+}
+
+func (w *WeixinStruct) GetTradeno(tp string) string {
+	lenRandom := 6 - len([]rune(tp)) //控制长度
+	tradeno := fmt.Sprintf("%s_%d%s%s", tp, time.Now().UnixNano(), util.GetRandom(lenRandom), util.GetLetterRandom(6))
+	tradeno = processTradeno(tradeno)
+	return tradeno
+}
+
+//tradeno a:打赏 b:pc端数据导出 c:移动端微信数据导出 C:app数据导出  d:微信端数据报告 D:app端数据报告
+func (w *WeixinStruct) CreatePrepayOrder(tradeno, ip, openid, detailmsg string, totalfee int) (string, *map[string]string) {
+	defer util.Catch()
+	//测试环境,1分钱
+	if config.Config.AllPayMoney > 0 {
+		totalfee = config.Config.AllPayMoney
+	}
+	notifyUrl := config.Config.WebDomain + "/jypay/callback/wxPay"
+	//获取支付标题
+	attachmsg, bodymsg := "", ""
+	if tradeno == WX_REWARD_JSAPI { //打赏
+		attachmsg, bodymsg = w.Dashang_attachmsg, w.Dashang_bodymsg
+	} else if tradeno == WX_DATAEXPORT_NATIVE || tradeno == WX_DATAEXPORT_JSAPI || tradeno == WX_DATAEXPORT_APP { //数据导出
+		attachmsg, bodymsg, detailmsg = w.sjdc_msg, w.sjdc_msg, w.sjdc_msg
+	} else if tradeno == WX_DATAREPORT_JSAPI || tradeno == WX_DATAREPORT_APP || tradeno == WX_DATAREPORT_NATIVE { //数据报告
+		attachmsg, bodymsg, detailmsg = w.Sjbg_msg, w.Sjbg_msg, w.Sjbg_msg
+	} else if tradeno == WX_SUBVIP_JSAPI || tradeno == WX_SUBVIP_APP || tradeno == WX_SUBVIP_NATIVE { //vip订阅
+		attachmsg, bodymsg, detailmsg = w.Subvip_msg, w.Subvip_msg, w.Subvip_msg
+	} else if tradeno == WX_COURSE_NATIVE || tradeno == WX_COURSE_APP || tradeno == WX_COURSE_JSAPI { //招投标课程
+		attachmsg, bodymsg, detailmsg = w.Course_msg, w.Course_msg, w.Course_msg
+	} else if tradeno == WX_ENTNICHE_APP || tradeno == WX_ENTNICHE_NATIVE {
+		attachmsg, bodymsg, detailmsg = w.Entniche_msg, w.Entniche_msg, w.Entniche_msg
+	} else if tradeno == WX_COURSEONLINE_APP || tradeno == WX_COURSEONLINE_JSAPI || tradeno == WX_COURSEONLINE_NATIVE {
+		attachmsg, bodymsg, detailmsg = w.CourseOnline_msg, w.CourseOnline_msg, w.CourseOnline_msg
+	} else if tradeno == WX_MEMBER_NATIVE || tradeno == WX_MEMBER_APP || tradeno == WX_MEMBER_JSAPI {
+		attachmsg, bodymsg, detailmsg = w.Member_msg, w.Member_msg, w.Member_msg
+	} else if tradeno == WX_AIFORECASTPACK_APP || tradeno == WX_AIFORECASTPACK_JSAPI || tradeno == WX_AIFORECASTPACK_NATIVE {
+		attachmsg, bodymsg, detailmsg = w.Aiforecastpage_msg, w.Aiforecastpage_msg, w.Aiforecastpage_msg
+	} else if tradeno == WX_BIDFILE_APP || tradeno == WX_BIDFILE_JSAPI || tradeno == WX_BIDFILE_NATIVE {
+		attachmsg, bodymsg, detailmsg = w.Bidfile_msg, w.Bidfile_msg, w.Bidfile_msg
+	} else if tradeno == WX_INTEGRAL_APP || tradeno == WX_INTEGRAL_JSAPI || tradeno == WX_INTEGRAL_NATIVE {
+		attachmsg, bodymsg, detailmsg = w.Integral_msg, w.Integral_msg, w.Integral_msg
+	} else if tradeno == WX_DATAPACK_APP || tradeno == WX_DATAPACK_JSAIP || tradeno == WX_DATAPACK_NATIVE {
+		attachmsg, bodymsg, detailmsg = w.DataPack_msg, w.DataPack_msg, w.DataPack_msg
+	} else if tradeno == WX_AREAPACK_APP || tradeno == WX_AREAPACK_JSAPI || tradeno == WX_AREAPACK_NATIVE {
+		attachmsg, bodymsg, detailmsg = w.AreaPack_msg, w.AreaPack_msg, w.AreaPack_msg
+	} else if tradeno == WX_FILEPACK_APP || tradeno == WX_FILEPACK_JSAPI || tradeno == WX_FILEPACK_NATIVE {
+		attachmsg, bodymsg, detailmsg = w.FilePack_msg, w.FilePack_msg, w.FilePack_msg
+	} else if tradeno == WX_BUYERPORTRAITPACK_APP || tradeno == WX_BUYERPORTRAITPACK_JSAPI || tradeno == WX_BUYERPORTRAITPACK_NATIVE {
+		attachmsg, bodymsg, detailmsg = w.BuyerPortraitPack_msg, w.BuyerPortraitPack_msg, w.BuyerPortraitPack_msg
+	}
+
+	//支付类型分类
+	tradeType, appid := "", w.Appid
+	if tradeno == WX_REWARD_JSAPI || tradeno == WX_DATAEXPORT_JSAPI || tradeno == WX_DATAREPORT_JSAPI || tradeno == WX_SUBVIP_JSAPI || tradeno == WX_COURSE_JSAPI || tradeno == WX_COURSEONLINE_JSAPI || tradeno == WX_MEMBER_JSAPI || tradeno == WX_AIFORECASTPACK_JSAPI || tradeno == WX_BIDFILE_JSAPI || tradeno == WX_INTEGRAL_JSAPI || tradeno == WX_DATAPACK_JSAIP || tradeno == WX_AREAPACK_JSAPI || tradeno == WX_FILEPACK_JSAPI || tradeno == WX_BUYERPORTRAITPACK_JSAPI { //微信js支付
+		tradeType = "JSAPI"
+	} else if tradeno == WX_DATAEXPORT_APP || tradeno == WX_DATAREPORT_APP || tradeno == WX_SUBVIP_APP || tradeno == WX_COURSE_APP || tradeno == WX_ENTNICHE_APP || tradeno == WX_COURSEONLINE_APP || tradeno == WX_MEMBER_APP || tradeno == WX_AIFORECASTPACK_APP || tradeno == WX_BIDFILE_APP || tradeno == WX_INTEGRAL_APP || tradeno == WX_DATAPACK_APP || tradeno == WX_AREAPACK_APP || tradeno == WX_FILEPACK_APP || tradeno == WX_BUYERPORTRAITPACK_APP { //app支付
+		tradeType = "APP"
+		appid = w.Appid_app
+	} else if tradeno == WX_DATAEXPORT_NATIVE || tradeno == WX_COURSE_NATIVE || tradeno == WX_COURSEONLINE_NATIVE || tradeno == WX_DATAREPORT_NATIVE || tradeno == WX_ENTNICHE_NATIVE || tradeno == WX_SUBVIP_NATIVE || tradeno == WX_MEMBER_NATIVE || tradeno == WX_AIFORECASTPACK_NATIVE || tradeno == WX_BIDFILE_NATIVE || tradeno == WX_INTEGRAL_NATIVE || tradeno == WX_DATAPACK_NATIVE || tradeno == WX_AREAPACK_NATIVE || tradeno == WX_FILEPACK_NATIVE || tradeno == WX_BUYERPORTRAITPACK_NATIVE { //扫码支付
+		tradeType = "NATIVE"
+	}
+	tradeno = w.GetTradeno(tradeno)
+	//测试环境微信支付需要转换对应的正式环境的openid
+	if w.OpenidSwitch != nil {
+		if oid, ok := (*(w.OpenidSwitch))[openid]; ok {
+			openid = oid.(string)
+		}
+	}
+	//获取预订单号
+	ret, _ := w.GetPrepayId(config.Config.Weixinrpc, map[string]string{
+		"attachmsg":  attachmsg,
+		"bodymsg":    bodymsg,
+		"detailmsg":  detailmsg,
+		"useropenid": openid,
+		"tradeno":    tradeno,
+		"userip":     ip,
+		"totalfee":   fmt.Sprint(totalfee),
+		"mchid":      w.Mchid,
+		"key":        w.Key,
+		"notifyUrl":  notifyUrl,
+		"appid":      appid,
+		"tradeType":  tradeType, //NATIVE JSAPI APP
+	})
+	return tradeno, ret
+}
+
+//取得预生成订单编号,我们的系统要控制下,别订单重复了
+func (w *WeixinStruct) GetPrepayId(weixinrpc string, param map[string]string) (res *map[string]string, e error) {
+	util.Try(func() {
+		client, err := rpc.DialHTTP("tcp", weixinrpc)
+		defer client.Close()
+		if err != nil {
+			e = err
+			log.Println(err.Error())
+			return
+		}
+		var ret []byte
+		err = client.Call("WeiXinRpc.GetPrepayId", param, &ret)
+		if err != nil {
+			e = err
+			log.Println(err.Error())
+		} else {
+			json.Unmarshal(ret, &res)
+		}
+	}, func(e interface{}) {})
+	return
+}
+
+//微信支付订单关闭
+func (w *WeixinStruct) CloseOrder(tradeno, payWay string) (r bool) {
+	util.Try(func() {
+		client, err := rpc.DialHTTP("tcp", config.Config.Weixinrpc)
+		defer client.Close()
+		if err != nil {
+			return
+		}
+		err = client.Call("WeiXinRpc.CloseOrder",
+			map[string]string{
+				"tradeno": tradeno,
+				"key":     w.Key,
+				"mchid":   w.Mchid,
+				"appid":   w.Appid,
+			}, &r)
+		if err != nil {
+			log.Println(err.Error())
+			return
+		}
+	}, func(e interface{}) {})
+	return r
+}
+
+//微信js支付获取支付参数
+func (w *WeixinStruct) GetWxjsPaySign(prepayid string) string {
+	timestamp := time.Now().Unix()
+	nonceStr := util.GetRandom(16) + util.GetLetterRandom(16)
+	sign := util.WxSign(fmt.Sprintf("appId=%s&nonceStr=%s&package=%s&signType=%s&timeStamp=%d&key=%s", w.Appid, nonceStr, "prepay_id="+prepayid, "MD5", timestamp, w.Key))
+	res := map[string]interface{}{
+		"appId":     w.Appid,
+		"timestamp": fmt.Sprint(timestamp),
+		"signType":  "MD5",
+		"sign":      sign,
+		"nonceStr":  nonceStr,
+		"prepayId":  "prepay_id=" + prepayid,
+	}
+	byteArr, _ := json.Marshal(res)
+	return string(byteArr)
+}
+
+//微信app支付 获取支付串
+func (wx *WeixinStruct) GetAppWxPayStr(prepayid string) string {
+	data := struct {
+		XMLName    xml.Name `xml:"xml"`
+		Appid      string   `xml:"appid"`
+		Partnerid  string   `xml:"partnerid"` //微信支付分配的商户号
+		Prepayid   string   `xml:"prepayid"`  //微信返回的支付交易会话ID
+		PackageStr string   `xml:"package"`   //暂填写固定值Sign=WXPay
+		Noncestr   string   `xml:"noncestr"`  //随机字符串,不长于32位。
+		Timestamp  string   `xml:"timestamp"` //时间戳
+		Sign       string   `xml:"sign"`      //签名
+	}{
+		Appid:      wx.Appid_app,
+		Partnerid:  wx.Mchid,
+		Prepayid:   prepayid,
+		PackageStr: "Sign=WXPay",
+		Noncestr:   util.GetRandom(16) + util.GetLetterRandom(16),
+		Timestamp:  fmt.Sprintf("%d", time.Now().Unix()),
+	}
+	data.Sign = util.CreateWxSign(fmt.Sprintf("&key=%s", wx.Key), data)
+
+	byteArr, err := json.Marshal(data)
+	if err != nil {
+		log.Printf("CreatePrepayOrder Marshal %v\n", err)
+		return ""
+	}
+	return string(byteArr)
+}
+
+//获取js支付参数(为方便存库返回string,前端需要转成json)
+func GetWxJsPayStr(prepayid string) string {
+	//获取订单支付字符串
+	nonceStr := util.GetRandom(16) + util.GetLetterRandom(16)
+	timestamp := time.Now().Unix()
+	sign := util.WxSign(fmt.Sprintf("appId=%s&nonceStr=%s&package=%s&signType=%s&timeStamp=%d&key=%s", WxStruct.Appid, nonceStr, "prepay_id="+prepayid, "MD5", timestamp, WxStruct.Key))
+	m := map[string]interface{}{
+		"appId":     WxStruct.Appid,
+		"timestamp": fmt.Sprint(timestamp),
+		"signType":  "MD5",
+		"sign":      sign,
+		"nonceStr":  nonceStr,
+		"prepayId":  "prepay_id=" + prepayid,
+	}
+	byteArr, err := json.Marshal(m)
+	if err != nil {
+		log.Println("GetWxJsPayStr ", err)
+		return ""
+	}
+	return string(byteArr)
+}

二進制
rpc/.DS_Store


+ 6 - 0
rpc/etc/paycenter.yaml

@@ -0,0 +1,6 @@
+Name: paycenter.rpc
+ListenOn: 127.0.0.1:8099
+Etcd:
+  Hosts:
+  - 127.0.0.1:2379
+  Key: paycenter.rpc

+ 7 - 0
rpc/internal/config/config.go

@@ -0,0 +1,7 @@
+package config
+
+import "github.com/zeromicro/go-zero/zrpc"
+
+type Config struct {
+	zrpc.RpcServerConf
+}

+ 32 - 0
rpc/internal/logic/getpaycodelogic.go

@@ -0,0 +1,32 @@
+package logic
+
+import (
+	"context"
+
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/internal/svc"
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/pb"
+
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/service"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetPayCodeLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetPayCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPayCodeLogic {
+	return &GetPayCodeLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取支付二维码
+func (l *GetPayCodeLogic) GetPayCode(in *pb.GetPayCodeReq) (*pb.GetPayCodeResp, error) {
+	// todo: add your logic here and delete this line
+
+	return service.GetPayCode(in), nil
+}

+ 28 - 0
rpc/internal/server/paycenterserver.go

@@ -0,0 +1,28 @@
+// Code generated by goctl. DO NOT EDIT!
+// Source: payCenter.proto
+
+package server
+
+import (
+	"context"
+
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/internal/logic"
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/internal/svc"
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/pb"
+)
+
+type PayCenterServer struct {
+	svcCtx *svc.ServiceContext
+}
+
+func NewPayCenterServer(svcCtx *svc.ServiceContext) *PayCenterServer {
+	return &PayCenterServer{
+		svcCtx: svcCtx,
+	}
+}
+
+// 获取支付二维码
+func (s *PayCenterServer) GetPayCode(ctx context.Context, in *pb.GetPayCodeReq) (*pb.GetPayCodeResp, error) {
+	l := logic.NewGetPayCodeLogic(ctx, s.svcCtx)
+	return l.GetPayCode(in)
+}

+ 13 - 0
rpc/internal/svc/servicecontext.go

@@ -0,0 +1,13 @@
+package svc
+
+import "bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/internal/config"
+
+type ServiceContext struct {
+	Config config.Config
+}
+
+func NewServiceContext(c config.Config) *ServiceContext {
+	return &ServiceContext{
+		Config: c,
+	}
+}

+ 28 - 0
rpc/payCenter.proto

@@ -0,0 +1,28 @@
+syntax = "proto3";
+
+option go_package ="./pb";
+
+message GetPayCodeReq {
+	string appid = 1;
+	string order_code = 2;
+	string pay_way = 3;
+	int64 money = 4;
+	string s_openid = 5;
+	string product_type = 6;
+}
+
+message GetPayCodeResp {
+	int64 error_code = 1;
+	string error_msg = 2;
+	PayCode data = 3;
+}
+
+message PayCode {
+	string code_url = 1;
+	string timeout = 2;
+}
+
+service PayCenter {
+	//获取支付二维码
+	rpc GetPayCode (GetPayCodeReq) returns (GetPayCodeResp);
+}

+ 38 - 0
rpc/paycenter.go

@@ -0,0 +1,38 @@
+// Code generated by goctl. DO NOT EDIT!
+// Source: payCenter.proto
+
+package main
+
+import (
+	"flag"
+	"fmt"
+
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/internal/server"
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/internal/svc"
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/pb"
+
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/internal/config"
+
+	"github.com/zeromicro/go-zero/core/conf"
+	"github.com/zeromicro/go-zero/zrpc"
+	"google.golang.org/grpc"
+)
+
+var configFile = flag.String("f", "etc/paycenter.yaml", "the config file")
+
+func main() {
+	flag.Parse()
+
+	var c config.Config
+	conf.MustLoad(*configFile, &c)
+	ctx := svc.NewServiceContext(c)
+	srv := server.NewPayCenterServer(ctx)
+
+	s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
+		pb.RegisterPayCenterServer(grpcServer, srv)
+	})
+	defer s.Stop()
+
+	fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
+	s.Start()
+}

+ 41 - 0
rpc/paycenter/paycenter.go

@@ -0,0 +1,41 @@
+// Code generated by goctl. DO NOT EDIT!
+// Source: payCenter.proto
+
+//go:generate mockgen -destination ./paycenter_mock.go -package paycenter -source $GOFILE
+
+package paycenter
+
+import (
+	"context"
+
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/pb"
+
+	"github.com/zeromicro/go-zero/zrpc"
+)
+
+type (
+	GetPayCodeReq  = pb.GetPayCodeReq
+	GetPayCodeResp = pb.GetPayCodeResp
+	PayCode        = pb.PayCode
+
+	PayCenter interface {
+		// 获取支付二维码
+		GetPayCode(ctx context.Context, in *GetPayCodeReq) (*GetPayCodeResp, error)
+	}
+
+	defaultPayCenter struct {
+		cli zrpc.Client
+	}
+)
+
+func NewPayCenter(cli zrpc.Client) PayCenter {
+	return &defaultPayCenter{
+		cli: cli,
+	}
+}
+
+// 获取支付二维码
+func (m *defaultPayCenter) GetPayCode(ctx context.Context, in *GetPayCodeReq) (*GetPayCodeResp, error) {
+	client := pb.NewPayCenterClient(m.cli.Conn())
+	return client.GetPayCode(ctx, in)
+}

+ 442 - 0
rpc/pb/payCenter.pb.go

@@ -0,0 +1,442 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.23.0
+// 	protoc        v3.11.4
+// source: payCenter.proto
+
+package pb
+
+import (
+	context "context"
+	proto "github.com/golang/protobuf/proto"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// This is a compile-time assertion that a sufficiently up-to-date version
+// of the legacy proto package is being used.
+const _ = proto.ProtoPackageIsVersion4
+
+type GetPayCodeReq struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Appid       string `protobuf:"bytes,1,opt,name=appid,proto3" json:"appid,omitempty"`
+	OrderCode   string `protobuf:"bytes,2,opt,name=order_code,json=orderCode,proto3" json:"order_code,omitempty"`
+	PayWay      string `protobuf:"bytes,3,opt,name=pay_way,json=payWay,proto3" json:"pay_way,omitempty"`
+	Money       int64  `protobuf:"varint,4,opt,name=money,proto3" json:"money,omitempty"`
+	SOpenid     string `protobuf:"bytes,5,opt,name=s_openid,json=sOpenid,proto3" json:"s_openid,omitempty"`
+	ProductType string `protobuf:"bytes,6,opt,name=product_type,json=productType,proto3" json:"product_type,omitempty"`
+}
+
+func (x *GetPayCodeReq) Reset() {
+	*x = GetPayCodeReq{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_payCenter_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetPayCodeReq) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetPayCodeReq) ProtoMessage() {}
+
+func (x *GetPayCodeReq) ProtoReflect() protoreflect.Message {
+	mi := &file_payCenter_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetPayCodeReq.ProtoReflect.Descriptor instead.
+func (*GetPayCodeReq) Descriptor() ([]byte, []int) {
+	return file_payCenter_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *GetPayCodeReq) GetAppid() string {
+	if x != nil {
+		return x.Appid
+	}
+	return ""
+}
+
+func (x *GetPayCodeReq) GetOrderCode() string {
+	if x != nil {
+		return x.OrderCode
+	}
+	return ""
+}
+
+func (x *GetPayCodeReq) GetPayWay() string {
+	if x != nil {
+		return x.PayWay
+	}
+	return ""
+}
+
+func (x *GetPayCodeReq) GetMoney() int64 {
+	if x != nil {
+		return x.Money
+	}
+	return 0
+}
+
+func (x *GetPayCodeReq) GetSOpenid() string {
+	if x != nil {
+		return x.SOpenid
+	}
+	return ""
+}
+
+func (x *GetPayCodeReq) GetProductType() string {
+	if x != nil {
+		return x.ProductType
+	}
+	return ""
+}
+
+type GetPayCodeResp struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	ErrorCode int64    `protobuf:"varint,1,opt,name=error_code,json=errorCode,proto3" json:"error_code,omitempty"`
+	ErrorMsg  string   `protobuf:"bytes,2,opt,name=error_msg,json=errorMsg,proto3" json:"error_msg,omitempty"`
+	Data      *PayCode `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
+}
+
+func (x *GetPayCodeResp) Reset() {
+	*x = GetPayCodeResp{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_payCenter_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetPayCodeResp) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetPayCodeResp) ProtoMessage() {}
+
+func (x *GetPayCodeResp) ProtoReflect() protoreflect.Message {
+	mi := &file_payCenter_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetPayCodeResp.ProtoReflect.Descriptor instead.
+func (*GetPayCodeResp) Descriptor() ([]byte, []int) {
+	return file_payCenter_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *GetPayCodeResp) GetErrorCode() int64 {
+	if x != nil {
+		return x.ErrorCode
+	}
+	return 0
+}
+
+func (x *GetPayCodeResp) GetErrorMsg() string {
+	if x != nil {
+		return x.ErrorMsg
+	}
+	return ""
+}
+
+func (x *GetPayCodeResp) GetData() *PayCode {
+	if x != nil {
+		return x.Data
+	}
+	return nil
+}
+
+type PayCode struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	CodeUrl string `protobuf:"bytes,1,opt,name=code_url,json=codeUrl,proto3" json:"code_url,omitempty"`
+	Timeout string `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout,omitempty"`
+}
+
+func (x *PayCode) Reset() {
+	*x = PayCode{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_payCenter_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PayCode) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PayCode) ProtoMessage() {}
+
+func (x *PayCode) ProtoReflect() protoreflect.Message {
+	mi := &file_payCenter_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use PayCode.ProtoReflect.Descriptor instead.
+func (*PayCode) Descriptor() ([]byte, []int) {
+	return file_payCenter_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *PayCode) GetCodeUrl() string {
+	if x != nil {
+		return x.CodeUrl
+	}
+	return ""
+}
+
+func (x *PayCode) GetTimeout() string {
+	if x != nil {
+		return x.Timeout
+	}
+	return ""
+}
+
+var File_payCenter_proto protoreflect.FileDescriptor
+
+var file_payCenter_proto_rawDesc = []byte{
+	0x0a, 0x0f, 0x70, 0x61, 0x79, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x22, 0xb1, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x43, 0x6f, 0x64, 0x65,
+	0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x05, 0x61, 0x70, 0x70, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64,
+	0x65, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f,
+	0x72, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x5f,
+	0x77, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x79, 0x57, 0x61,
+	0x79, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03,
+	0x52, 0x05, 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x5f, 0x6f, 0x70, 0x65,
+	0x6e, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x4f, 0x70, 0x65, 0x6e,
+	0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x74, 0x79,
+	0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63,
+	0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x6a, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x43,
+	0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72,
+	0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x65, 0x72, 0x72,
+	0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f,
+	0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x72, 0x72, 0x6f, 0x72,
+	0x4d, 0x73, 0x67, 0x12, 0x1c, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x08, 0x2e, 0x50, 0x61, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74,
+	0x61, 0x22, 0x3e, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x19, 0x0a, 0x08,
+	0x63, 0x6f, 0x64, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
+	0x63, 0x6f, 0x64, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f,
+	0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75,
+	0x74, 0x32, 0x3a, 0x0a, 0x09, 0x50, 0x61, 0x79, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x2d,
+	0x0a, 0x0a, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x2e, 0x47,
+	0x65, 0x74, 0x50, 0x61, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x47,
+	0x65, 0x74, 0x50, 0x61, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x42, 0x06, 0x5a,
+	0x04, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_payCenter_proto_rawDescOnce sync.Once
+	file_payCenter_proto_rawDescData = file_payCenter_proto_rawDesc
+)
+
+func file_payCenter_proto_rawDescGZIP() []byte {
+	file_payCenter_proto_rawDescOnce.Do(func() {
+		file_payCenter_proto_rawDescData = protoimpl.X.CompressGZIP(file_payCenter_proto_rawDescData)
+	})
+	return file_payCenter_proto_rawDescData
+}
+
+var file_payCenter_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_payCenter_proto_goTypes = []interface{}{
+	(*GetPayCodeReq)(nil),  // 0: GetPayCodeReq
+	(*GetPayCodeResp)(nil), // 1: GetPayCodeResp
+	(*PayCode)(nil),        // 2: PayCode
+}
+var file_payCenter_proto_depIdxs = []int32{
+	2, // 0: GetPayCodeResp.data:type_name -> PayCode
+	0, // 1: PayCenter.GetPayCode:input_type -> GetPayCodeReq
+	1, // 2: PayCenter.GetPayCode:output_type -> GetPayCodeResp
+	2, // [2:3] is the sub-list for method output_type
+	1, // [1:2] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_payCenter_proto_init() }
+func file_payCenter_proto_init() {
+	if File_payCenter_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_payCenter_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetPayCodeReq); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_payCenter_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetPayCodeResp); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_payCenter_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PayCode); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_payCenter_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   3,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_payCenter_proto_goTypes,
+		DependencyIndexes: file_payCenter_proto_depIdxs,
+		MessageInfos:      file_payCenter_proto_msgTypes,
+	}.Build()
+	File_payCenter_proto = out.File
+	file_payCenter_proto_rawDesc = nil
+	file_payCenter_proto_goTypes = nil
+	file_payCenter_proto_depIdxs = nil
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConnInterface
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion6
+
+// PayCenterClient is the client API for PayCenter service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type PayCenterClient interface {
+	//获取支付二维码
+	GetPayCode(ctx context.Context, in *GetPayCodeReq, opts ...grpc.CallOption) (*GetPayCodeResp, error)
+}
+
+type payCenterClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewPayCenterClient(cc grpc.ClientConnInterface) PayCenterClient {
+	return &payCenterClient{cc}
+}
+
+func (c *payCenterClient) GetPayCode(ctx context.Context, in *GetPayCodeReq, opts ...grpc.CallOption) (*GetPayCodeResp, error) {
+	out := new(GetPayCodeResp)
+	err := c.cc.Invoke(ctx, "/PayCenter/GetPayCode", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// PayCenterServer is the server API for PayCenter service.
+type PayCenterServer interface {
+	//获取支付二维码
+	GetPayCode(context.Context, *GetPayCodeReq) (*GetPayCodeResp, error)
+}
+
+// UnimplementedPayCenterServer can be embedded to have forward compatible implementations.
+type UnimplementedPayCenterServer struct {
+}
+
+func (*UnimplementedPayCenterServer) GetPayCode(context.Context, *GetPayCodeReq) (*GetPayCodeResp, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetPayCode not implemented")
+}
+
+func RegisterPayCenterServer(s *grpc.Server, srv PayCenterServer) {
+	s.RegisterService(&_PayCenter_serviceDesc, srv)
+}
+
+func _PayCenter_GetPayCode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(GetPayCodeReq)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(PayCenterServer).GetPayCode(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/PayCenter/GetPayCode",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(PayCenterServer).GetPayCode(ctx, req.(*GetPayCodeReq))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _PayCenter_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "PayCenter",
+	HandlerType: (*PayCenterServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "GetPayCode",
+			Handler:    _PayCenter_GetPayCode_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "payCenter.proto",
+}

+ 94 - 0
service/pay.go

@@ -0,0 +1,94 @@
+package service
+
+import (
+	"encoding/base64"
+	"errors"
+
+	"bp.jydev.jianyu360.cn/BaseService/payCenter/pay"
+	. "bp.jydev.jianyu360.cn/BaseService/payCenter/rpc/paycenter"
+	"github.com/SKatiyar/qr"
+)
+
+func GetPayCode(this *GetPayCodeReq) *GetPayCodeResp {
+	// Appid
+	// OrderCode
+	// PayWay
+	// Money
+	// SOpenid
+	// ProductType
+	// userId := qutil.ObjToString(this.GetSession("userId"))
+
+	// query := map[string]interface{}{
+	// 	"user_id":      userId,
+	// 	"order_code":   orderCode,
+	// 	"order_status": 0,
+	// }
+	// oData := util.Mysql.FindOne("dataexport_order", query, "id,code_url,prepay_time,pay_way,order_money,product_type,out_trade_no,course_status", "")
+	// if oData == nil || len(*oData) == 0 {
+	// 	return &entity.FuncResult{false, errors.New("未知订单"), nil}
+	// }
+	// //对公转账审核中
+	// if qutil.IntAll((*oData)["course_status"]) == 2 || qutil.IntAll((*oData)["course_status"]) == 4 {
+	// 	return &entity.FuncResult{false, errors.New("转账审核中,请勿重复支付"), nil}
+	// }
+	// payParam := qutil.ObjToString((*oData)["code_url"])
+	// payway := qutil.ObjToString((*oData)["pay_way"])
+	// totalfee := qutil.IntAll((*oData)["order_money"])
+	totalfee := this.Money
+	//上次订单支付串是否可用
+	// if prepayTime, err := time.ParseInLocation(qutil.Date_Full_Layout, qutil.ObjToString((*oData)["prepay_time"]), time.Local); err == nil && payParam != "" {
+	// 	if time.Now().Before(prepayTime.Add(time.Hour * 2)) { //支付串未过期
+	// 		if payway == payway_req {
+	// 			if payParam != "" {
+	// 				return &entity.FuncResult{true, nil, map[string]interface{}{
+	// 					"orderCode": orderCode,
+	// 					"res":       payParam,
+	// 					"price":     totalfee,
+	// 					"timeout":   prepayTime.Unix() + 2*60*60 - time.Now().Unix(),
+	// 					"payWay":    payway}}
+	// 			}
+	// 		} else {
+	// 			//关闭之前支付串(防止重复支付)
+	// 			if !pay.CloseOrder(qutil.ObjToString((*oData)["out_trade_no"]), payway) {
+	// 				log.Printf("GetCommonPayParam 关闭订单%s出错\n", orderCode)
+	// 				return &entity.FuncResult{false, errors.New("支付方式切换异常"), nil}
+	// 			}
+	// 		}
+	// 	}
+	// }
+	//重新生成支付串
+	// productFlag, ok := pay.PayWayAndSign[qutil.ObjToString((*oData)["product_type"])][payway_req]
+	productFlag, ok := pay.PayWayAndSign[this.ProductType][this.PayWay]
+	if !ok {
+		return &entity.FuncResult{false, errors.New("未知支付类型:" + payway_req), nil}
+	}
+	tradeno, prepayid, payParam, err := pay.CreateOrderPay(totalfee, productFlag, this.IP(), this.SOpenid, this.PayWay)
+	if err != nil {
+		return &entity.FuncResult{false, errors.New(fmt.Sprintf("创建支付失败[%v]", err)), nil}
+	}
+	//更新订单表
+	// now := time.Now()
+	// ok = util.Mysql.Update("dataexport_order", query, map[string]interface{}{
+	// 	"code_url":     payParam,
+	// 	"prepay_time":  qutil.FormatDate(&now, qutil.Date_Full_Layout),
+	// 	"out_trade_no": tradeno,
+	// 	"prepay_id":    prepayid,
+	// 	"pay_way":      payway_req,
+	// })
+	// if !ok {
+	// 	return &entity.FuncResult{false, errors.New("数据库操作异常"), nil}
+	// }
+	if this.PayWay == "wx_pc" && payParam != nil {
+		rEncode, _ := qr.Encode(payParam, qr.M)
+		pngdat := rEncode.PNG()
+		payParam = base64.StdEncoding.EncodeToString(pngdat)
+	}
+	return &GetPayCodeResp{
+		ErrorCode: 1,
+		ErrorMsg:  errStr,
+		Data: &PayCode{
+			CodeUrl: payParam,
+			Timeout: 2 * 3600,
+		},
+	}
+}