|
@@ -0,0 +1,288 @@
|
|
|
+package wechat
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "encoding/xml"
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+
|
|
|
+ "git.chuangxin1.com/cx/util"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ // WePayHost base host
|
|
|
+ WePayHost = `https://api.mch.weixin.qq.com`
|
|
|
+
|
|
|
+ // WePayURLUnifiedOrder pay
|
|
|
+ WePayURLUnifiedOrder = `/pay/unifiedorder`
|
|
|
+ // WePayURLUnifiedQuery order query
|
|
|
+ WePayURLUnifiedQuery = `/pay/orderquery`
|
|
|
+
|
|
|
+ // WePayTradeTypeJS JSAPI 公众号支付
|
|
|
+ WePayTradeTypeJS = `JSAPI`
|
|
|
+ // WePayTradeTypeNative NATIVE 扫码支付
|
|
|
+ WePayTradeTypeNative = `NATIVE`
|
|
|
+ // WePayTradeTypeAPP APP APP支付
|
|
|
+ WePayTradeTypeAPP = `APP`
|
|
|
+
|
|
|
+ // WeOrderPaySuccess pay ok
|
|
|
+ WeOrderPaySuccess = 0
|
|
|
+)
|
|
|
+
|
|
|
+// WePayConfig WeChat pay configure
|
|
|
+type WePayConfig struct {
|
|
|
+ AppID string `json:"appid"`
|
|
|
+ MchID string `json:"mchid"`
|
|
|
+ Key string `json:"key"`
|
|
|
+ AppSecret string `json:"appsecret"`
|
|
|
+ SSLCert string `json:"sslcert"`
|
|
|
+ SSLKey string `json:"sslkey"`
|
|
|
+ NotifyURL string `json:"notify_url"`
|
|
|
+}
|
|
|
+
|
|
|
+// UnifiedOrder https://api.mch.weixin.qq.com/pay/unifiedorder
|
|
|
+type UnifiedOrder struct {
|
|
|
+ XMLName xml.Name `xml:"xml"`
|
|
|
+ AppID string `xml:"appid"`
|
|
|
+ MchID string `xml:"mch_id"`
|
|
|
+ Body string `xml:"body"`
|
|
|
+ NonceStr string `xml:"nonce_str"`
|
|
|
+ NotifyURL string `xml:"notify_url"`
|
|
|
+ TradeType string `xml:"trade_type"`
|
|
|
+ OpenID string `xml:"openid"`
|
|
|
+ SpbillCreateIP string `xml:"spbill_create_ip"`
|
|
|
+ TimeStart string `xml:"time_start"`
|
|
|
+ TotalFee int `xml:"total_fee"`
|
|
|
+ OutTradeNo string `xml:"out_trade_no"`
|
|
|
+ Attach string `xml:"attach"`
|
|
|
+ Sign string `xml:"sign"`
|
|
|
+}
|
|
|
+
|
|
|
+// OrderQuery https://api.mch.weixin.qq.com/pay/orderquery
|
|
|
+type OrderQuery struct {
|
|
|
+ XMLName xml.Name `xml:"xml"`
|
|
|
+ AppID string `xml:"appid"`
|
|
|
+ MchID string `xml:"mch_id"`
|
|
|
+ NonceStr string `xml:"nonce_str"`
|
|
|
+ TransactionID string `xml:"transaction_id,omitempty"`
|
|
|
+ OutTradeNo string `xml:"out_trade_no,omitempty"`
|
|
|
+ Sign string `xml:"sign"`
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+<xml><appid><![CDATA[wx0e9caf2ab1fcd508]]></appid>
|
|
|
+<attach><![CDATA[邦泰商旅]]></attach>
|
|
|
+<bank_type><![CDATA[CITIC_CREDIT]]></bank_type>
|
|
|
+<cash_fee><![CDATA[349000]]></cash_fee>
|
|
|
+<fee_type><![CDATA[CNY]]></fee_type>
|
|
|
+<is_subscribe><![CDATA[N]]></is_subscribe>
|
|
|
+<mch_id><![CDATA[1340131101]]></mch_id>
|
|
|
+<nonce_str><![CDATA[0gwbd25n61bxkw6m64kj4ovf60i2q2sg]]></nonce_str>
|
|
|
+<openid><![CDATA[o7-PevpkcTqeJOkOF4X7VFhX_SwY]]></openid>
|
|
|
+<out_trade_no><![CDATA[134013110120180709134447]]></out_trade_no>
|
|
|
+<result_code><![CDATA[SUCCESS]]></result_code>
|
|
|
+<return_code><![CDATA[SUCCESS]]></return_code>
|
|
|
+<sign><![CDATA[8E581E734384DB2EA6FC08B1482BCCFC]]></sign>
|
|
|
+<time_end><![CDATA[20180709134502]]></time_end>
|
|
|
+<total_fee>349000</total_fee>
|
|
|
+<trade_type><![CDATA[JSAPI]]></trade_type>
|
|
|
+<transaction_id><![CDATA[4200000120201807096318807841]]></transaction_id>
|
|
|
+</xml>
|
|
|
+
|
|
|
+<xml>
|
|
|
+ <return_code>SUCCESS</return_code>
|
|
|
+ <return_msg></return_msg>
|
|
|
+ <appid>wx84998d72abc5970a</appid>
|
|
|
+ <mch_id>1511843111</mch_id>
|
|
|
+ <nonce_str>1534475690</nonce_str>
|
|
|
+ <sign>27577C258DD809DCE0F8A515233494D1</sign>
|
|
|
+ <result_code>SUCCESS</result_code>
|
|
|
+ <prepay_id></prepay_id>
|
|
|
+ <trade_type>JSAPI</trade_type>
|
|
|
+ <code_url></code_url>
|
|
|
+ <err_code></err_code>
|
|
|
+ <err_code_des></err_code_des>
|
|
|
+ <openid>opCl81S2TyHPx7IJRZxc2yGrom5E</openid>
|
|
|
+ <attach>{"PlaceNo":"603009","VNO":"","Start":"2018-08-17 11:14:50"</attach>
|
|
|
+ <is_subscribe>Y</is_subscribe>
|
|
|
+ <bank_type>CFT</bank_type>
|
|
|
+ <cash_fee>2</cash_fee>
|
|
|
+ <fee_type>CNY</fee_type>
|
|
|
+ <out_trade_no>1534475690957535612</out_trade_no>
|
|
|
+ <time_end>20180817111506</time_end>
|
|
|
+ <total_fee>2</total_fee>
|
|
|
+ <trade_state></trade_state>
|
|
|
+ <trade_state_desc></trade_state_desc>
|
|
|
+ <transaction_id>4200000159201808174345835044</transaction_id>
|
|
|
+</xml>
|
|
|
+// */
|
|
|
+
|
|
|
+// FormWePayNotify notify
|
|
|
+type FormWePayNotify struct {
|
|
|
+ XMLName xml.Name `xml:"xml" json:"_,omitempty"`
|
|
|
+ AppID string `form:"appid" xml:"appid"`
|
|
|
+ Attach string `form:"attach" xml:"attach"`
|
|
|
+ BankType string `form:"bank_type" xml:"bank_type"`
|
|
|
+ CashFee int `form:"cash_fee" xml:"cash_fee"`
|
|
|
+ FeeType string `form:"fee_type" xml:"fee_type"`
|
|
|
+
|
|
|
+ MchID string `form:"mch_id" xml:"mch_id"`
|
|
|
+ IsSubscribe string `form:"is_subscribe" xml:"is_subscribe"`
|
|
|
+ NonceStr string `form:"nonce_str" xml:"nonce_str"`
|
|
|
+ OpenID string `form:"openid" xml:"openid"`
|
|
|
+ OutTradeNo string `form:"out_trade_no" xml:"out_trade_no"`
|
|
|
+
|
|
|
+ ResultCode string `form:"result_code" xml:"result_code"`
|
|
|
+ ReturnMsg string `form:"return_msg" xml:"return_msg"`
|
|
|
+ ReturnCode string `form:"return_code" xml:"return_code"`
|
|
|
+ ErrCodeDes string `form:"err_code_des" xml:"err_code_des"`
|
|
|
+ ErrCode string `form:"err_code" xml:"err_code"`
|
|
|
+
|
|
|
+ Sign string `form:"sign" xml:"sign"`
|
|
|
+ TimeEnd string `form:"time_end" xml:"time_end"`
|
|
|
+ TotalFee int `form:"total_fee" xml:"total_fee"`
|
|
|
+
|
|
|
+ TradeType string `form:"trade_type" xml:"trade_type"`
|
|
|
+ TransactionID string `form:"transaction_id" xml:"transaction_id"`
|
|
|
+}
|
|
|
+
|
|
|
+// WePayReply pay reply
|
|
|
+type WePayReply struct {
|
|
|
+ XMLName xml.Name `xml:"xml" json:"_,omitempty"`
|
|
|
+ ReturnCode string `xml:"return_code"`
|
|
|
+ ReturnMsg string `xml:"return_msg"`
|
|
|
+ AppID string `xml:"appid"`
|
|
|
+ MchID string `xml:"mch_id"`
|
|
|
+ NonceStr string `xml:"nonce_str"`
|
|
|
+ Sign string `xml:"sign"`
|
|
|
+ ResultCode string `xml:"result_code"`
|
|
|
+ PrePayID string `xml:"prepay_id"`
|
|
|
+ TradeType string `xml:"trade_type"`
|
|
|
+ URL string `xml:"code_url"`
|
|
|
+ ErrCode string `xml:"err_code"`
|
|
|
+ ErrCodeDes string `xml:"err_code_des"`
|
|
|
+ OpenID string `xml:"openid"`
|
|
|
+ Attach string `xml:"attach"`
|
|
|
+ IsSubscribe string `xml:"is_subscribe"`
|
|
|
+ BankType string `xml:"bank_type"`
|
|
|
+ CashFee string `xml:"cash_fee"`
|
|
|
+ FeeType string `xml:"fee_type"`
|
|
|
+ OutTradeNo string `xml:"out_trade_no"`
|
|
|
+ TimeEnd string `xml:"time_end"`
|
|
|
+ TotalFee string `xml:"total_fee"`
|
|
|
+ TradeState string `xml:"trade_state"`
|
|
|
+ TradeStateDesc string `xml:"trade_state_desc"`
|
|
|
+ TransactionID string `xml:"transaction_id"`
|
|
|
+}
|
|
|
+
|
|
|
+var (
|
|
|
+ // WePayTradeState 交易状态
|
|
|
+ WePayTradeState = map[string]int{"SUCCESS": 0, "REFUND": 2, "NOTPAY": 3, "CLOSED": 4, "REVOKED": 5, "USERPAYING": 6, "PAYERROR": 7}
|
|
|
+)
|
|
|
+
|
|
|
+// PayCheck check reply
|
|
|
+func PayCheck(reply WePayReply) (ok bool, err error) {
|
|
|
+ // 通信是否正确
|
|
|
+ if reply.ReturnCode == "FAIL" {
|
|
|
+ err = errors.New(reply.ReturnMsg)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 交易是否成功
|
|
|
+ if reply.ResultCode == "FAIL" {
|
|
|
+ err = errors.New(reply.ErrCodeDes)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ ok = true
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// PayNotifyCheck check reply
|
|
|
+func PayNotifyCheck(reply FormWePayNotify) (ok bool, err error) {
|
|
|
+ // 通信是否正确
|
|
|
+ if reply.ReturnCode == "FAIL" {
|
|
|
+ err = errors.New(reply.ReturnMsg)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 交易是否成功
|
|
|
+ if reply.ResultCode == "FAIL" {
|
|
|
+ err = errors.New(reply.ErrCodeDes)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ ok = true
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// PayQuery order query
|
|
|
+func PayQuery(cpyid int, config WePayConfig, order OrderQuery) (reply WePayReply, err error) {
|
|
|
+ m := make(map[string]interface{})
|
|
|
+
|
|
|
+ m["appid"] = order.AppID
|
|
|
+ m["mch_id"] = order.MchID
|
|
|
+ m["out_trade_no"] = order.OutTradeNo
|
|
|
+ m["nonce_str"] = order.NonceStr
|
|
|
+ //m["transaction_id"] = order.TransactionID
|
|
|
+
|
|
|
+ var (
|
|
|
+ data []byte
|
|
|
+ )
|
|
|
+
|
|
|
+ order.Sign = Sign(m, config.Key)
|
|
|
+ data, err = xml.Marshal(order)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ reply, err = post(WePayHost+WePayURLUnifiedQuery, data)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// Pay pay
|
|
|
+func Pay(config WePayConfig, order UnifiedOrder) (reply WePayReply, err error) {
|
|
|
+ m := make(map[string]interface{})
|
|
|
+
|
|
|
+ m["appid"] = order.AppID
|
|
|
+ m["body"] = order.Body
|
|
|
+ m["mch_id"] = order.MchID
|
|
|
+ m["notify_url"] = order.NotifyURL
|
|
|
+ m["trade_type"] = order.TradeType
|
|
|
+ m["spbill_create_ip"] = order.SpbillCreateIP
|
|
|
+ m["total_fee"] = order.TotalFee
|
|
|
+ m["out_trade_no"] = order.OutTradeNo
|
|
|
+ m["nonce_str"] = order.NonceStr
|
|
|
+ m["attach"] = order.Attach
|
|
|
+ m["time_start"] = order.TimeStart
|
|
|
+ m["openid"] = order.OpenID
|
|
|
+
|
|
|
+ var data []byte
|
|
|
+
|
|
|
+ order.Sign = Sign(m, config.Key)
|
|
|
+ if data, err = xml.Marshal(order); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ reply, err = post(WePayHost+WePayURLUnifiedOrder, data)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func post(url string, data []byte) (reply WePayReply, err error) {
|
|
|
+ var (
|
|
|
+ msg util.Message
|
|
|
+ headers = make(map[string]string)
|
|
|
+ )
|
|
|
+
|
|
|
+ headers["Accept"] = "application/xml"
|
|
|
+ headers["Content-Type"] = "application/xml; charset=utf-8"
|
|
|
+ msg, err = util.Post(url, headers, bytes.NewReader(data))
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ fmt.Println("************Response************")
|
|
|
+ fmt.Println(string(msg.Body))
|
|
|
+ fmt.Println("************ResponseEnd************")
|
|
|
+ err = xml.Unmarshal(msg.Body, &reply)
|
|
|
+
|
|
|
+ return
|
|
|
+}
|