ls 5 years ago
parent
commit
988b300464
6 changed files with 239 additions and 66 deletions
  1. 6 0
      binding/form_mapping.go
  2. 2 18
      tcp/server.go
  3. 179 25
      unionpay/entrust.go
  4. 41 23
      unionpay/rsa.go
  5. 8 0
      unionpay/url.go
  6. 3 0
      wechat/wechat.go

+ 6 - 0
binding/form_mapping.go

@@ -6,11 +6,17 @@ package binding
 
 import (
 	"errors"
+	"net/url"
 	"reflect"
 	"strconv"
 	"time"
 )
 
+// MapForm form values map to struct
+func MapForm(ptr interface{}, values url.Values) error {
+	return mapForm(ptr, values)
+}
+
 func mapForm(ptr interface{}, form map[string][]string) error {
 	typ := reflect.TypeOf(ptr).Elem()
 	val := reflect.ValueOf(ptr).Elem()

+ 2 - 18
tcp/server.go

@@ -11,11 +11,8 @@ import (
 
 // NewTCPServer make TCP server
 func NewTCPServer(addr string, cb func(*net.TCPConn)) {
-	var (
-		//buffer bytes.Buffer
-		logger = log.New(os.Stdout, "INFO: ", log.Lshortfile)
-	)
-	// Interrupt handler.
+	logger := log.New(os.Stdout, "INFO: ", log.LstdFlags)
+
 	errc := make(chan error)
 	go func() {
 		c := make(chan os.Signal)
@@ -38,8 +35,6 @@ func NewTCPServer(addr string, cb func(*net.TCPConn)) {
 
 	go func() {
 		logger.Println("TCP Listen", addr)
-		//log..Log("Protocol", "HTTP", "addr", addr)
-		//errc <- server.ListenAndServe()
 		for {
 			conn, err := listen.AcceptTCP()
 			if err != nil {
@@ -48,20 +43,9 @@ func NewTCPServer(addr string, cb func(*net.TCPConn)) {
 				break
 			}
 			go cb(conn)
-			/*
-				tcpConn, ok := conn.(*net.TCPConn)
-				if !ok {
-					//error handle
-					conn.Close()
-					continue
-				}
-				go cb(tcpConn)
-				// */
 		}
 		logger.Println("Exit server", "Quit")
 	}()
 
-	// Run!
-	//logger.Log("Exit", <-errc)
 	logger.Println("TCP Service Exit", <-errc)
 }

+ 179 - 25
unionpay/entrust.go

@@ -3,11 +3,32 @@
 package unionpay
 
 import (
+	"bytes"
+	"crypto/rsa"
+	"crypto/sha256"
+	"crypto/x509"
 	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"net/url"
+	"sort"
+
+	"git.chuangxin1.com/cx/util/binding"
+
+	"git.chuangxin1.com/cx/util"
 )
 
+// MerchantInfo 商家信息
+type MerchantInfo struct {
+	MerID   string // 商户代码
+	OrderID string // 商户订单号,8-32位数字字母,不能含“-”或“_”
+	TxnTime string // 订单发送时间,格式为YYYYMMDDhhmmss,取北京时间
+}
+
 // CustomerInfo 客户信息
 type CustomerInfo struct {
+	CardNo       string `form:"cardNo" json:"cardNo" xml:"cardNo"`             // 卡号
 	Phone        string `form:"phone" json:"phone" xml:"phone"`                // 手机号
 	CertifTp     string `form:"certifTp" json:"certifTp" xml:"certifTp"`       // 证件类型, 01 - 身份证
 	CertifID     string `form:"certifId" json:"certifId" xml:"certifId"`       // 证件号,15位身份证不校验尾号,18位会校验尾号
@@ -16,42 +37,175 @@ type CustomerInfo struct {
 	Expired      string `form:"expired" json:"expired" xml:"expired"`          // 有效期,YYMM格式,持卡人卡面印的是MMYY的,请注意代码设置倒一下
 }
 
-func sign() {
+// ReqRes 请求返回
+type ReqRes struct {
+	TxnType     string `form:"txnType" json:"txnType" xml:"txnType"`
+	RespCode    string `form:"respCode" json:"respCode" xml:"respCode"`
+	ChannelType string `form:"channelType" json:"channelType" xml:"channelType"`
+	MerID       string `form:"merId" json:"merId" xml:"merId"`
+
+	TxnSubType     string `form:"txnSubType" json:"txnSubType" xml:"txnSubType"`
+	CustomerInfo   string `form:"customerInfo" json:"customerInfo" xml:"customerInfo"`
+	Version        string `form:"version" json:"version" xml:"version"`
+	SignPubKeyCert string `form:"signPubKeyCert" json:"signPubKeyCert" xml:"signPubKeyCert"`
 
+	SignMethod string `form:"signMethod" json:"signMethod" xml:"signMethod"`
+	AccNo      string `form:"accNo" json:"accNo" xml:"accNo"`
+	CertID     string `form:"certId" json:"certId" xml:"certId"`
+	Encoding   string `form:"encoding" json:"encoding" xml:"encoding"`
+
+	RespMsg       string `form:"respMsg" json:"respMsg" xml:"respMsg"`
+	BizType       string `form:"bizType" json:"bizType" xml:"bizType"`
+	EncryptCertID string `form:"encryptCertId" json:"encryptCertId" xml:"encryptCertId"`
+	Signature     string `form:"signature" json:"signature" xml:"signature"`
+
+	OrderID    string `form:"orderId" json:"orderId" xml:"orderId"`
+	TxnTime    string `form:"txnTime" json:"txnTime" xml:"txnTime"`
+	AccessType int    `form:"accessType" json:"accessType" xml:"accessType"`
 }
 
-// filename 证书路径
-func s(filename string, cardNo string, customerInfo CustomerInfo) {
-	cert, data, e := GetCertificate(filename)
-	if e != nil {
+// Sign 签名
+//   encryptCert 公钥证书路径
+//   signCert 签名证书路径
+func Sign(encryptCert, signCert string, mi MerchantInfo, ci CustomerInfo) (err error) {
+	var (
+		cert     = &x509.Certificate{}
+		priveKey = &rsa.PrivateKey{}
+		accNo    []byte
+	)
+	cert, _, err = GetCertificate(encryptCert)
+	if err != nil {
+		return
+	}
+
+	priveKey, err = GetPrivateKey(signCert)
+	if err != nil {
+		return
+	}
+	args := url.Values{}
+
+	args.Add("version", "5.1.0")                          // 版本号
+	args.Add("encoding", "utf-8")                         // 编码方式
+	args.Add("signMethod", "01")                          // 签名方法
+	args.Add("txnType", "72")                             // 交易类型
+	args.Add("txnSubType", "11")                          // 交易子类
+	args.Add("bizType", "000501")                         // 业务类型
+	args.Add("accessType", "0")                           // 接入类型
+	args.Add("channelType", "07")                         // 渠道类型
+	args.Add("certId", cert.SerialNumber.String())        // 证书序列号
+	args.Add("encryptCertId", cert.SerialNumber.String()) // 验签证书序列号
+
+	args.Add("merId", mi.MerID)     // 商户代码
+	args.Add("orderId", mi.OrderID) // 商户订单号,8-32位数字字母,不能含“-”或“_”
+	args.Add("txnTime", mi.TxnTime) // 订单发送时间,格式为YYYYMMDDhhmmss,取北京时间
+
+	publickey := cert.PublicKey.(*rsa.PublicKey)
+	accNo, err = RSAEncrypt(publickey, []byte(ci.CardNo))
+	if err != nil {
 		return
 	}
-	var args = make(map[string]interface{})
+	args.Add("accNo", base64.StdEncoding.EncodeToString(accNo)) // 卡号,敏感信息加密方式
 
-	args["version"] = "5.1.0"                          // 版本号
-	args["encoding"] = "utf-8"                         // 编码方式
-	args["signMethod"] = "01"                          // 签名方法
-	args["txnType"] = "72"                             // 交易类型
-	args["txnSubType"] = "11"                          // 交易子类
-	args["bizType"] = "000501"                         // 业务类型
-	args["accessType"] = "0"                           // 接入类型
-	args["channelType"] = "07"                         // 渠道类型
-	args["encryptCertId"] = cert.SerialNumber.String() // 验签证书序列号
+	fmt.Println("CardNo", ci.CardNo)
 
-	args["merId"] = ""   // 商户代码
-	args["orderId"] = "" // 商户订单号,8-32位数字字母,不能含“-”或“_”
-	args["txnTime"] = "" // 订单发送时间,格式为YYYYMMDDhhmmss,取北京时间
+	var cardNo []byte
+	cardNo, err = RSADecrypt(priveKey, accNo)
+	fmt.Println("CardNo", ci.CardNo)
+	fmt.Println("De CardNo", string(cardNo))
 
-	accNo, err := RSAEncrypt(data, []byte(cardNo))
+	// 持卡人身份信息,敏感信息加密方式
+	var phone []byte
+	phone, err = RSAEncrypt(publickey, []byte(ci.Phone))
 	if err != nil {
 		return
 	}
-	args["accNo"] = string(accNo) // 卡号,敏感信息加密方式
+	encryptedInfo := `phone=` + base64.StdEncoding.EncodeToString(phone)
+	if len(ci.Cvn2) > 0 {
+		var cvn2 []byte
+		cvn2, err = RSAEncrypt(publickey, []byte(ci.Cvn2))
+		if err != nil {
+			return
+		}
+		encryptedInfo += `cvn2=` + base64.StdEncoding.EncodeToString(cvn2)
+	}
+	if len(ci.Expired) > 0 {
+		var expired []byte
+		expired, err = RSAEncrypt(publickey, []byte(ci.Cvn2))
+		if err != nil {
+			return
+		}
+		encryptedInfo += `expired=` + base64.StdEncoding.EncodeToString(expired)
+	}
 
-	kvs := `phone=` + customerInfo.Phone
-	kvs += `&certifTp=` + customerInfo.CertifTp
-	kvs += `&certifId=` + customerInfo.CertifID
-	kvs += `&customerNm=` + customerInfo.CustomerName // `&cvn2=&=expired`
+	kvs := `encryptedInfo=` + encryptedInfo
+	kvs += `&certifTp=` + ci.CertifTp
+	kvs += `&certifId=` + ci.CertifID
+	kvs += `&customerNm=` + ci.CustomerName // `&cvn2=&=expired`
 	info := `{` + kvs + `}`
-	args["customerInfo"] = base64.StdEncoding.EncodeToString([]byte(info)) // 持卡人身份信息,敏感信息不加密方式
+	args.Add("customerInfo", base64.StdEncoding.EncodeToString([]byte(info))) // 持卡人身份信息,敏感信息加密方式
+
+	keys := make([]string, 0)
+
+	for k := range args {
+		keys = append(keys, k)
+	}
+
+	sort.Strings(keys)
+
+	var s string
+	for _, k := range keys {
+		for _, value := range args[k] {
+			if value != "" {
+				s = s + "&" + k + "=" + value
+			}
+		}
+	}
+
+	fmt.Println(s[1:])
+	hash := sha256.New()
+	hash.Write([]byte(s[1:]))
+	fmt.Println("sha1", hex.EncodeToString(hash.Sum(nil)))
+
+	var signature []byte
+	signature, err = RSASign256(priveKey, hash.Sum(nil))
+	if err != nil {
+		return
+	}
+	s = base64.StdEncoding.EncodeToString(signature)
+	fmt.Println("signature", s)
+	args.Add("signature", s) // 签名
+	fmt.Println(args.Encode())
+
+	headers := make(map[string]string)
+
+	headers["Accept"] = "text/html; charset=UTF-8"
+	headers["Accept-Encoding"] = "gzip, deflate, br"
+	headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"
+	var msg util.Message
+	msg, err = util.Post(BaseURL+ReqBackTrans, "", "", headers, bytes.NewReader([]byte(args.Encode())))
+	if err != nil {
+		return
+	}
+	fmt.Println("*************************************")
+	fmt.Println(BaseURL + ReqBackTrans)
+	fmt.Println(msg.StatusCode)
+	fmt.Println(msg.Header)
+	fmt.Println(string(msg.Body))
+	fmt.Println("*************************************")
+	var (
+		res    ReqRes
+		values url.Values
+	)
+	values, err = url.ParseQuery(string(msg.Body))
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+	err = binding.MapForm(&res, values)
+	fmt.Println(err)
+	if err == nil {
+		bs, _ := json.Marshal(&res)
+		fmt.Println(string(bs))
+	}
+	return
 }

+ 41 - 23
unionpay/rsa.go

@@ -1,44 +1,38 @@
 package unionpay
 
 import (
+	"crypto"
 	"crypto/rand"
 	"crypto/rsa"
 	"crypto/x509"
 	"encoding/pem"
 	"errors"
+	"fmt"
 	"io/ioutil"
 )
 
 // RSAEncrypt RSA 加密
-func RSAEncrypt(publicKey, src []byte) (data []byte, err error) {
-	block, _ := pem.Decode(publicKey)
-	if block == nil {
-		err = errors.New(`public key error`)
-		return
-	}
-	pubI, e := x509.ParsePKIXPublicKey(block.Bytes)
-	if e != nil {
-		err = e
-		return
-	}
-	pub := pubI.(*rsa.PublicKey)
-	data, err = rsa.EncryptPKCS1v15(rand.Reader, pub, src)
+func RSAEncrypt(publicKey *rsa.PublicKey, src []byte) (data []byte, err error) {
+	data, err = rsa.EncryptPKCS1v15(rand.Reader, publicKey, src)
 	return
 }
 
 // RSADecrypt RSA 解密
-func RSADecrypt(priveKey, ciphertext []byte) (data []byte, err error) {
-	block, _ := pem.Decode(priveKey)
-	if block == nil {
-		err = errors.New(`priveKey key error`)
-		return
-	}
-	priv, e := x509.ParsePKCS1PrivateKey(block.Bytes)
-	if e != nil {
-		err = e
+func RSADecrypt(priveKey *rsa.PrivateKey, ciphertext []byte) (data []byte, err error) {
+	data, err = rsa.DecryptPKCS1v15(rand.Reader, priveKey, ciphertext)
+
+	return
+}
+
+// RSASign256 RSA sha256 签名
+func RSASign256(priveKey *rsa.PrivateKey, src []byte) (data []byte, err error) {
+	h := crypto.Hash.New(crypto.SHA256)
+	_, err = h.Write(src)
+	if err != nil {
 		return
 	}
-	rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
+	hashed := h.Sum(nil)
+	data, err = rsa.SignPKCS1v15(rand.Reader, priveKey, crypto.SHA256, hashed)
 	return
 }
 
@@ -58,3 +52,27 @@ func GetCertificate(filename string) (cert *x509.Certificate, data []byte, err e
 	cert, err = x509.ParseCertificate(block.Bytes)
 	return
 }
+
+// GetPrivateKey 获取私钥信息
+func GetPrivateKey(filename string) (key *rsa.PrivateKey, err error) {
+	var (
+		bs      []byte
+		private interface{}
+	)
+	bs, err = ioutil.ReadFile(filename)
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+	block, _ := pem.Decode(bs)
+	if block == nil {
+		err = errors.New(`pem Decode error`)
+		return
+	}
+	private, err = x509.ParsePKCS8PrivateKey(block.Bytes)
+	if err != nil {
+		return
+	}
+	key = private.(*rsa.PrivateKey)
+	return
+}

+ 8 - 0
unionpay/url.go

@@ -1 +1,9 @@
 package unionpay
+
+const (
+	// BaseURL 主机地址
+	BaseURL = `https://gateway.95516.com`
+
+	// ReqBackTrans 建立委托:后台交易,只有同步应答
+	ReqBackTrans = `/gateway/api/backTransReq.do`
+)

+ 3 - 0
wechat/wechat.go

@@ -198,6 +198,7 @@ func postXML(url string, data []byte) (res Response, err error) {
 		headers = make(map[string]string)
 	)
 
+	headers["Accept-Encoding"] = "gzip, deflate, br"
 	headers["Accept"] = "application/json"
 	headers["Content-Type"] = "application/xml; charset=utf-8"
 	msg, err = util.Post(url, "", "", headers, bytes.NewReader(data))
@@ -214,6 +215,7 @@ func postJSON(url string, data []byte) (res Response, err error) {
 		headers = make(map[string]string)
 	)
 
+	headers["Accept-Encoding"] = "gzip, deflate, br"
 	headers["Accept"] = "application/json"
 	headers["Content-Type"] = "application/json; charset=utf-8"
 	msg, err = util.Post(url, "", "", headers, bytes.NewReader(data))
@@ -230,6 +232,7 @@ func postBody(uri string, data []byte) (body []byte, err error) {
 		headers = make(map[string]string)
 	)
 
+	headers["Accept-Encoding"] = "gzip, deflate, br"
 	headers["Accept"] = "application/json"
 	headers["Content-Type"] = "application/json; charset=utf-8"
 	msg, err = util.Post(uri, "", "", headers, bytes.NewReader(data))