From 4f5945fb0fb1a3ba19b7adbe6644cc30f15c2093 Mon Sep 17 00:00:00 2001 From: sunyaqiu Date: Sat, 6 Apr 2019 14:52:05 +0800 Subject: [PATCH 1/4] add refund --- pay/refund.go | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 pay/refund.go diff --git a/pay/refund.go b/pay/refund.go new file mode 100644 index 0000000..10d313c --- /dev/null +++ b/pay/refund.go @@ -0,0 +1,180 @@ +package pay + +import ( + "bytes" + "crypto/tls" + "encoding/pem" + "encoding/xml" + "fmt" + "github.com/akikistyle/wechat/util" + "golang.org/x/crypto/pkcs12" + "io/ioutil" + "log" + "net/http" +) + +var refundGateway = "https://api.mch.weixin.qq.com/secapi/pay/refund" + +//Refund Parameter +type RefundParams struct { + TransactionId string + OutRefundNo string + TotalFee string + RefundFee string + RefundDesc string + RootCa string //ca证书 +} + +//Refund request +type refundRequest struct { + AppID string `xml:"appid"` + MchID string `xml:"mch_id"` + NonceStr string `xml:"nonce_str"` + Sign string `xml:"sign"` + SignType string `xml:"sign_type,omitempty"` + TransactionId string `xml:"transaction_id"` + OutRefundNo string `xml:"out_refund_no"` + TotalFee string `xml:"total_fee"` + RefundFee string `xml:"refund_fee"` + RefundDesc string `xml:"refund_desc,omitempty"` + //NotifyUrl string `xml:"notify_url,omitempty"` +} + +//Refund Response +type RefundResponse struct { + ReturnCode string `xml:"return_code"` + ReturnMsg string `xml:"return_msg"` + AppID string `xml:"appid,omitempty"` + MchID string `xml:"mch_id,omitempty"` + NonceStr string `xml:"nonce_str,omitempty"` + Sign string `xml:"sign,omitempty"` + ResultCode string `xml:"result_code,omitempty"` + ErrCode string `xml:"err_code,omitempty"` + ErrCodeDes string `xml:"err_code_des,omitempty"` + TransactionId string `xml:"transaction_id,omitempty"` + OutTradeNo string `xml:"out_trade_no,omitempty"` + OutRefundNo string `xml:"out_refund_no,omitempty"` + RefundId string `xml:"refund_id,omitempty"` + RefundFee string `xml:"refund_fee,omitempty"` + SettlementRefundFee string `xml:"settlement_refund_fee,omitempty"` + TotalFee string `xml:"total_fee,omitempty"` + SettlementTotalFee string `xml:"settlement_total_fee,omitempty"` + FeeType string `xml:"fee_type,omitempty"` + CashFee string `xml:"cash_fee,omitempty"` + CashFeeType string `xml:"cash_fee_type,omitempty"` +} + +//退款申请 +func (pcf *Pay) Refund(p *RefundParams) (rsp RefundResponse, err error) { + nonceStr := util.RandomStr(32) + param := make(map[string]interface{}) + param["appid"] = pcf.AppID + param["mch_id"] = pcf.PayMchID + param["nonce_str"] = nonceStr + param["out_refund_no"] = p.OutRefundNo + param["refund_desc"] = p.RefundDesc + param["refund_fee"] = p.RefundFee + param["total_fee"] = p.TotalFee + param["sign_type"] = "MD5" + param["transaction_id"] = p.TransactionId + + bizKey := "&key=" + pcf.PayKey + str := orderParam(param, bizKey) + sign := util.MD5Sum(str) + request := refundRequest{ + AppID: pcf.AppID, + MchID: pcf.PayMchID, + NonceStr: nonceStr, + Sign: sign, + SignType: "MD5", + TransactionId: p.TransactionId, + OutRefundNo: p.OutRefundNo, + TotalFee: p.TotalFee, + RefundFee: p.RefundFee, + RefundDesc: p.RefundDesc, + } + rawRet, err := postXMLWithTLS(refundGateway, request, p.RootCa, pcf.PayMchID) + if err != nil { + return + } + err = xml.Unmarshal(rawRet, &rsp) + if err != nil { + return + } + if rsp.ReturnCode == "SUCCESS" { + if rsp.ResultCode == "SUCCESS" { + err = nil + return + } + err = fmt.Errorf("refund error, errcode=%s,errmsg=%s", rsp.ErrCode, rsp.ErrCodeDes) + return + } + err = fmt.Errorf("[msg : xmlUnmarshalError] [rawReturn : %s] [params : %s] [sign : %s]", + string(rawRet), str, sign) + return +} + +//http TLS +func httpWithTLS(rootCa, key string) (*http.Client, error) { + var client *http.Client + certData, err := ioutil.ReadFile(rootCa) + if err != nil { + return nil, fmt.Errorf("unable to find cert path=%s, error=%v", rootCa, err) + } + cert := pkcs12ToPem(certData, key) + config := &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + tr := &http.Transport{ + TLSClientConfig: config, + DisableCompression: true, + } + client = &http.Client{Transport: tr} + return client, nil +} + +//将Pkcs12转成Pem +func pkcs12ToPem(p12 []byte, password string) tls.Certificate { + blocks, err := pkcs12.ToPEM(p12, password) + defer func() { + if x := recover(); x != nil { + log.Print(x) + } + }() + if err != nil { + panic(err) + } + var pemData []byte + for _, b := range blocks { + pemData = append(pemData, pem.EncodeToMemory(b)...) + } + cert, err := tls.X509KeyPair(pemData, pemData) + if err != nil { + panic(err) + } + return cert +} + +//Post XML with TLS +func postXMLWithTLS(uri string, obj interface{}, ca, key string) ([]byte, error) { + xmlData, err := xml.Marshal(obj) + if err != nil { + return nil, err + } + + body := bytes.NewBuffer(xmlData) + client, err := httpWithTLS(ca, key) + if err != nil { + return nil, err + } + response, err := client.Post(uri, "application/xml;charset=utf-8", body) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return nil, fmt.Errorf("http code error : uri=%v , statusCode=%v", uri, response.StatusCode) + } + return ioutil.ReadAll(response.Body) +} From 02b3fcc6487c097aebf25e828ff916802042658a Mon Sep 17 00:00:00 2001 From: sunyaqiu Date: Sat, 6 Apr 2019 14:59:38 +0800 Subject: [PATCH 2/4] add refund --- pay/refund.go | 74 +-------------------------------------------------- util/http.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 73 deletions(-) diff --git a/pay/refund.go b/pay/refund.go index 10d313c..18c71f0 100644 --- a/pay/refund.go +++ b/pay/refund.go @@ -1,16 +1,9 @@ package pay import ( - "bytes" - "crypto/tls" - "encoding/pem" "encoding/xml" "fmt" "github.com/akikistyle/wechat/util" - "golang.org/x/crypto/pkcs12" - "io/ioutil" - "log" - "net/http" ) var refundGateway = "https://api.mch.weixin.qq.com/secapi/pay/refund" @@ -93,7 +86,7 @@ func (pcf *Pay) Refund(p *RefundParams) (rsp RefundResponse, err error) { RefundFee: p.RefundFee, RefundDesc: p.RefundDesc, } - rawRet, err := postXMLWithTLS(refundGateway, request, p.RootCa, pcf.PayMchID) + rawRet, err := util.PostXMLWithTLS(refundGateway, request, p.RootCa, pcf.PayMchID) if err != nil { return } @@ -113,68 +106,3 @@ func (pcf *Pay) Refund(p *RefundParams) (rsp RefundResponse, err error) { string(rawRet), str, sign) return } - -//http TLS -func httpWithTLS(rootCa, key string) (*http.Client, error) { - var client *http.Client - certData, err := ioutil.ReadFile(rootCa) - if err != nil { - return nil, fmt.Errorf("unable to find cert path=%s, error=%v", rootCa, err) - } - cert := pkcs12ToPem(certData, key) - config := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - tr := &http.Transport{ - TLSClientConfig: config, - DisableCompression: true, - } - client = &http.Client{Transport: tr} - return client, nil -} - -//将Pkcs12转成Pem -func pkcs12ToPem(p12 []byte, password string) tls.Certificate { - blocks, err := pkcs12.ToPEM(p12, password) - defer func() { - if x := recover(); x != nil { - log.Print(x) - } - }() - if err != nil { - panic(err) - } - var pemData []byte - for _, b := range blocks { - pemData = append(pemData, pem.EncodeToMemory(b)...) - } - cert, err := tls.X509KeyPair(pemData, pemData) - if err != nil { - panic(err) - } - return cert -} - -//Post XML with TLS -func postXMLWithTLS(uri string, obj interface{}, ca, key string) ([]byte, error) { - xmlData, err := xml.Marshal(obj) - if err != nil { - return nil, err - } - - body := bytes.NewBuffer(xmlData) - client, err := httpWithTLS(ca, key) - if err != nil { - return nil, err - } - response, err := client.Post(uri, "application/xml;charset=utf-8", body) - if err != nil { - return nil, err - } - defer response.Body.Close() - - if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("http code error : uri=%v , statusCode=%v", uri, response.StatusCode) - } - return ioutil.ReadAll(response.Body) -} diff --git a/util/http.go b/util/http.go index 6881052..bc3ed8d 100644 --- a/util/http.go +++ b/util/http.go @@ -2,11 +2,15 @@ package util import ( "bytes" + "crypto/tls" "encoding/json" + "encoding/pem" "encoding/xml" "fmt" + "golang.org/x/crypto/pkcs12" "io" "io/ioutil" + "log" "mime/multipart" "net/http" "os" @@ -141,3 +145,68 @@ func PostXML(uri string, obj interface{}) ([]byte, error) { } return ioutil.ReadAll(response.Body) } + +//http TLS +func httpWithTLS(rootCa, key string) (*http.Client, error) { + var client *http.Client + certData, err := ioutil.ReadFile(rootCa) + if err != nil { + return nil, fmt.Errorf("unable to find cert path=%s, error=%v", rootCa, err) + } + cert := pkcs12ToPem(certData, key) + config := &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + tr := &http.Transport{ + TLSClientConfig: config, + DisableCompression: true, + } + client = &http.Client{Transport: tr} + return client, nil +} + +//将Pkcs12转成Pem +func pkcs12ToPem(p12 []byte, password string) tls.Certificate { + blocks, err := pkcs12.ToPEM(p12, password) + defer func() { + if x := recover(); x != nil { + log.Print(x) + } + }() + if err != nil { + panic(err) + } + var pemData []byte + for _, b := range blocks { + pemData = append(pemData, pem.EncodeToMemory(b)...) + } + cert, err := tls.X509KeyPair(pemData, pemData) + if err != nil { + panic(err) + } + return cert +} + +//Post XML with TLS +func PostXMLWithTLS(uri string, obj interface{}, ca, key string) ([]byte, error) { + xmlData, err := xml.Marshal(obj) + if err != nil { + return nil, err + } + + body := bytes.NewBuffer(xmlData) + client, err := httpWithTLS(ca, key) + if err != nil { + return nil, err + } + response, err := client.Post(uri, "application/xml;charset=utf-8", body) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return nil, fmt.Errorf("http code error : uri=%v , statusCode=%v", uri, response.StatusCode) + } + return ioutil.ReadAll(response.Body) +} From f4f1860e678814aa459b65d4bc0673021c6728f2 Mon Sep 17 00:00:00 2001 From: sunyaqiu Date: Sat, 6 Apr 2019 15:24:40 +0800 Subject: [PATCH 3/4] fix some comment and struct field --- pay/refund.go | 20 ++++++++++---------- util/http.go | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pay/refund.go b/pay/refund.go index 18c71f0..b401990 100644 --- a/pay/refund.go +++ b/pay/refund.go @@ -8,9 +8,9 @@ import ( var refundGateway = "https://api.mch.weixin.qq.com/secapi/pay/refund" -//Refund Parameter +//RefundParams 调用参数 type RefundParams struct { - TransactionId string + TransactionID string OutRefundNo string TotalFee string RefundFee string @@ -18,14 +18,14 @@ type RefundParams struct { RootCa string //ca证书 } -//Refund request +//refundRequest 接口请求参数 type refundRequest struct { AppID string `xml:"appid"` MchID string `xml:"mch_id"` NonceStr string `xml:"nonce_str"` Sign string `xml:"sign"` SignType string `xml:"sign_type,omitempty"` - TransactionId string `xml:"transaction_id"` + TransactionID string `xml:"transaction_id"` OutRefundNo string `xml:"out_refund_no"` TotalFee string `xml:"total_fee"` RefundFee string `xml:"refund_fee"` @@ -33,7 +33,7 @@ type refundRequest struct { //NotifyUrl string `xml:"notify_url,omitempty"` } -//Refund Response +//RefundResponse 接口返回 type RefundResponse struct { ReturnCode string `xml:"return_code"` ReturnMsg string `xml:"return_msg"` @@ -44,10 +44,10 @@ type RefundResponse struct { ResultCode string `xml:"result_code,omitempty"` ErrCode string `xml:"err_code,omitempty"` ErrCodeDes string `xml:"err_code_des,omitempty"` - TransactionId string `xml:"transaction_id,omitempty"` + TransactionID string `xml:"transaction_id,omitempty"` OutTradeNo string `xml:"out_trade_no,omitempty"` OutRefundNo string `xml:"out_refund_no,omitempty"` - RefundId string `xml:"refund_id,omitempty"` + RefundID string `xml:"refund_id,omitempty"` RefundFee string `xml:"refund_fee,omitempty"` SettlementRefundFee string `xml:"settlement_refund_fee,omitempty"` TotalFee string `xml:"total_fee,omitempty"` @@ -57,7 +57,7 @@ type RefundResponse struct { CashFeeType string `xml:"cash_fee_type,omitempty"` } -//退款申请 +//Refund 退款申请 func (pcf *Pay) Refund(p *RefundParams) (rsp RefundResponse, err error) { nonceStr := util.RandomStr(32) param := make(map[string]interface{}) @@ -69,7 +69,7 @@ func (pcf *Pay) Refund(p *RefundParams) (rsp RefundResponse, err error) { param["refund_fee"] = p.RefundFee param["total_fee"] = p.TotalFee param["sign_type"] = "MD5" - param["transaction_id"] = p.TransactionId + param["transaction_id"] = p.TransactionID bizKey := "&key=" + pcf.PayKey str := orderParam(param, bizKey) @@ -80,7 +80,7 @@ func (pcf *Pay) Refund(p *RefundParams) (rsp RefundResponse, err error) { NonceStr: nonceStr, Sign: sign, SignType: "MD5", - TransactionId: p.TransactionId, + TransactionID: p.TransactionID, OutRefundNo: p.OutRefundNo, TotalFee: p.TotalFee, RefundFee: p.RefundFee, diff --git a/util/http.go b/util/http.go index bc3ed8d..086e65f 100644 --- a/util/http.go +++ b/util/http.go @@ -146,7 +146,7 @@ func PostXML(uri string, obj interface{}) ([]byte, error) { return ioutil.ReadAll(response.Body) } -//http TLS +//httpWithTLS CA证书 func httpWithTLS(rootCa, key string) (*http.Client, error) { var client *http.Client certData, err := ioutil.ReadFile(rootCa) @@ -165,7 +165,7 @@ func httpWithTLS(rootCa, key string) (*http.Client, error) { return client, nil } -//将Pkcs12转成Pem +//pkcs12ToPem 将Pkcs12转成Pem func pkcs12ToPem(p12 []byte, password string) tls.Certificate { blocks, err := pkcs12.ToPEM(p12, password) defer func() { @@ -187,7 +187,7 @@ func pkcs12ToPem(p12 []byte, password string) tls.Certificate { return cert } -//Post XML with TLS +//PostXMLWithTLS:Post XML with TLS func PostXMLWithTLS(uri string, obj interface{}, ca, key string) ([]byte, error) { xmlData, err := xml.Marshal(obj) if err != nil { From e66652f4b5d81017422dfd4717bd0212577f31d7 Mon Sep 17 00:00:00 2001 From: sunyaqiu Date: Sat, 6 Apr 2019 15:28:31 +0800 Subject: [PATCH 4/4] fix comment --- util/http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/http.go b/util/http.go index 086e65f..2376155 100644 --- a/util/http.go +++ b/util/http.go @@ -187,7 +187,7 @@ func pkcs12ToPem(p12 []byte, password string) tls.Certificate { return cert } -//PostXMLWithTLS:Post XML with TLS +//PostXMLWithTLS perform a HTTP/POST request with XML body and TLS func PostXMLWithTLS(uri string, obj interface{}, ca, key string) ([]byte, error) { xmlData, err := xml.Marshal(obj) if err != nil {