diff --git a/platformapi/wxpay/callback.go b/platformapi/wxpay/callback.go
index d80f2328..46571ad6 100644
--- a/platformapi/wxpay/callback.go
+++ b/platformapi/wxpay/callback.go
@@ -1,17 +1,16 @@
package wxpay
import (
- "bytes"
- "crypto/md5"
"encoding/base64"
- "encoding/binary"
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
+ "strings"
"git.rosy.net.cn/baseapi/utils"
"github.com/clbanning/mxj"
+ "github.com/nanjishidu/gomini/gocrypto"
)
type CData string
@@ -69,7 +68,7 @@ type RefundReqInfo struct {
OutRefundNo string `json:"out_refund_no"`
OutTradeNo string `json:"out_trade_no"`
RefundAccount string `json:"refund_account"`
- RefundFee string `json:"refund_fee"`
+ RefundFee string `json:"refund_fee" xml:"refund_fee"`
RefundID string `json:"refund_id"`
RefundRecvAccout string `json:"refund_recv_accout"`
RefundRequestSource string `json:"refund_request_source"`
@@ -94,7 +93,8 @@ type RefundResultMsg struct {
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
- ReqInfo *RefundReqInfo `json:"req_info,omitempty" xml:"req_info,omitempty"`
+ ReqInfoStr string `json:"req_info,omitempty"`
+ ReqInfoObj *RefundReqInfo `json:"req_info_obj,omitempty"`
}
type CallbackMsg struct {
@@ -127,13 +127,12 @@ func Err2CallbackResponse(err error, data string) *CallbackResponse {
func (a *API) decodeReqInfo(msg string) (decryptedMsg string, err error) {
binMsg, err := base64.StdEncoding.DecodeString(msg)
if err == nil {
- aesKey := []byte(fmt.Sprintf("%x", md5.Sum([]byte(a.appKey))))
- binResult, err2 := utils.AESCBCDecpryt(binMsg, aesKey, aesKey[:16])
+ // aesKey := []byte(fmt.Sprintf("%x", md5.Sum([]byte(a.appKey))))
+ // binResult, err2 := utils.AESCBCDecpryt(binMsg, aesKey, aesKey[:16])
+ gocrypto.SetAesKey(strings.ToLower(gocrypto.Md5(a.appKey)))
+ binResult, err2 := gocrypto.AesECBDecrypt(binMsg)
if err = err2; err == nil {
- var msgLen int32
- if err = binary.Read(bytes.NewBuffer(binResult[16:]), binary.BigEndian, &msgLen); err == nil {
- decryptedMsg = string(binResult[16+4 : 16+4+msgLen])
- }
+ decryptedMsg = string(binResult)
}
}
return decryptedMsg, err
@@ -144,15 +143,15 @@ func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackR
if err != nil {
return nil, Err2CallbackResponse(err, "")
}
- mapData, _, err := a.checkResultAsMap(string(data))
+ return a.getCallbackMsg(string(data))
+}
+
+func (a *API) getCallbackMsg(msgBody string) (msg *CallbackMsg, callbackResponse *CallbackResponse) {
+ mapData, _, err := a.checkResultAsMap(msgBody)
if err != nil {
return nil, Err2CallbackResponse(err, "")
}
- sign := utils.Interface2String(mapData[sigKey])
- desiredSign := a.signParam(mapData)
- if desiredSign != sign {
- return nil, Err2CallbackResponse(fmt.Errorf("desiredSign:%s <> sign:%s", desiredSign, sign), "")
- }
+
msg = &CallbackMsg{
MapData: mapData,
}
@@ -163,13 +162,15 @@ func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackR
ReturnMsg: utils.Interface2String(mapData["return_msg"]),
}
} else {
- if transactionID := utils.Interface2String(mapData["transaction_id"]); transactionID != "" {
- msg.MsgType = MsgTypePay
- var payResult *PayResultMsg
- if err = utils.Map2StructByJson(mapData, &payResult, false); err == nil {
- msg.Data = payResult
+ reqInfo := utils.Interface2String(mapData["req_info"])
+ if reqInfo == "" {
+ sign := utils.Interface2String(mapData[sigKey])
+ desiredSign := a.signParam(mapData)
+ if desiredSign != sign {
+ return nil, Err2CallbackResponse(fmt.Errorf("desiredSign:%s <> sign:%s", desiredSign, sign), "")
}
- } else if reqInfo := utils.Interface2String(mapData["req_info"]); reqInfo != "" {
+ }
+ if reqInfo != "" {
msg.MsgType = MsgTypeRefund
var refundResult *RefundResultMsg
if err = utils.Map2StructByJson(mapData, &refundResult, false); err == nil {
@@ -177,12 +178,18 @@ func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackR
mv, err2 := mxj.NewMapXml([]byte(reqInfo))
if err = err2; err == nil {
reqInfoMap := mv["root"].(map[string]interface{})
- if err = utils.Map2StructByJson(reqInfoMap, &refundResult.ReqInfo, false); err == nil {
+ if err = utils.Map2StructByJson(reqInfoMap, &refundResult.ReqInfoObj, false); err == nil {
msg.Data = refundResult
}
}
}
}
+ } else if transactionID := utils.Interface2String(mapData["transaction_id"]); transactionID != "" {
+ msg.MsgType = MsgTypePay
+ var payResult *PayResultMsg
+ if err = utils.Map2StructByJson(mapData, &payResult, false); err == nil {
+ msg.Data = payResult
+ }
}
}
if err != nil {
diff --git a/platformapi/wxpay/callback_test.go b/platformapi/wxpay/callback_test.go
new file mode 100644
index 00000000..ccdb5f65
--- /dev/null
+++ b/platformapi/wxpay/callback_test.go
@@ -0,0 +1,35 @@
+package wxpay
+
+import (
+ "strings"
+ "testing"
+
+ "git.rosy.net.cn/baseapi/utils"
+)
+
+func TestPayCallback(t *testing.T) {
+ bodyMsg := strings.Replace(`
+ \n\n\n\n\n\n\n\n\n\n\n\n\n1\n\n\n
+
+
+
+ `, "\\n", "\n", -1)
+ result, err := api.getCallbackMsg(bodyMsg)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(utils.Format4Output(result, false))
+}
+
+func TestPayRefundCallback(t *testing.T) {
+ bodyMsg := strings.Replace(`
+ SUCCESS
+
+
+ `, "\\n", "\n", -1)
+ result, err := api.getCallbackMsg(bodyMsg)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(utils.Format4Output(result, false))
+}
diff --git a/platformapi/wxpay/wxpay.go b/platformapi/wxpay/wxpay.go
index a79d1f9c..8dcb64ee 100644
--- a/platformapi/wxpay/wxpay.go
+++ b/platformapi/wxpay/wxpay.go
@@ -3,6 +3,7 @@ package wxpay
import (
"bytes"
"crypto/md5"
+ "crypto/tls"
"encoding/xml"
"fmt"
"net/http"
@@ -16,8 +17,8 @@ import (
)
const (
- prodURL = "https://api.mch.weixin.qq.com/pay"
- sandboxURL = "https://api.mch.weixin.qq.com/sandboxnew/pay"
+ prodURL = "https://api.mch.weixin.qq.com"
+ sandboxURL = "https://api.mch.weixin.qq.com/sandboxnew"
)
const (
@@ -169,6 +170,46 @@ type CreateOrderResult struct {
CodeURL string `json:"code_url"`
}
+type PayRefundParam struct {
+ RequestBase
+
+ TransactionID string `json:"transaction_id,omitempty" xml:"transaction_id,omitempty"`
+ OutTradeNo string `json:"out_trade_no,omitempty" xml:"out_trade_no,omitempty"`
+ OutRefundNo string `json:"out_refund_no" xml:"out_refund_no"`
+ TotalFee int `json:"total_fee" xml:"total_fee"`
+ RefundFee int `json:"refund_fee" xml:"refund_fee"`
+
+ RefundFeeType string `json:"refund_fee_type,omitempty" xml:"refund_fee_type,omitempty"`
+ RefundDesc CData `json:"refund_desc,omitempty" xml:"refund_desc,omitempty"`
+ NotifyURL string `json:"notify_url,omitempty" xml:"notify_url,omitempty"`
+}
+
+type PayRefundResult struct {
+ ReturnCode string `json:"return_code" xml:"return_code"`
+ ReturnMsg string `json:"return_msg" xml:"return_msg"`
+
+ AppID string `json:"appid" xml:"appid"`
+ DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
+ MchID string `json:"mch_id" xml:"mch_id"`
+ NonceStr string `json:"nonce_str" xml:"nonce_str"`
+ Sign string `json:"sign" xml:"sign"`
+ ResultCode string `json:"result_code" xml:"result_code"`
+ ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
+ ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
+
+ CashFee string `json:"cash_fee"`
+ CashRefundFee string `json:"cash_refund_fee"`
+ CouponRefundCount string `json:"coupon_refund_count"`
+ CouponRefundFee string `json:"coupon_refund_fee"`
+ OutRefundNo string `json:"out_refund_no"`
+ OutTradeNo string `json:"out_trade_no"`
+ RefundChannel string `json:"refund_channel"`
+ RefundFee string `json:"refund_fee"`
+ RefundID string `json:"refund_id"`
+ TotalFee string `json:"total_fee"`
+ TransactionID string `json:"transaction_id"`
+}
+
func New(appID, appKey, mchID string, config ...*platformapi.APIConfig) *API {
curConfig := platformapi.DefAPIConfig
if len(config) > 0 {
@@ -183,6 +224,19 @@ func New(appID, appKey, mchID string, config ...*platformapi.APIConfig) *API {
}
}
+func NewWithCertificate(appID, appKey, mchID string, certPEMBlock, keyPEMBlock []byte, config ...*platformapi.APIConfig) (a *API) {
+ certs, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
+ if err == nil {
+ a = New(appID, appKey, mchID, config...)
+ a.client.Transport = &http.Transport{
+ TLSClientConfig: &tls.Config{
+ Certificates: []tls.Certificate{certs},
+ },
+ }
+ }
+ return a
+}
+
func (a *API) GetAppID() string {
return a.appID
}
@@ -237,6 +291,12 @@ func (a *API) AccessAPI(action string, requestParam IRequestBase) (retVal map[st
retVal, errLevel, err = a.checkResultAsMap(jsonResult1[platformapi.KeyData].(string))
return errLevel, err
})
+ if err == nil {
+ if utils.Interface2String(retVal["result_code"]) != ResponseCodeSuccess {
+ err = utils.NewErrorCode(utils.Interface2String(retVal["err_code_des"]), utils.Interface2String(retVal["err_code"]))
+ retVal = nil
+ }
+ }
return retVal, err
}
@@ -251,13 +311,6 @@ func (a *API) checkResultAsMap(xmlStr string) (result map[string]interface{}, er
errLevel = platformapi.ErrLevelGeneralFail
err = utils.NewErrorCode(utils.Interface2String(result["return_msg"]), returnCode)
result = nil
- } else {
- // if utils.Interface2String(result["result_code"]) != ResponseCodeSuccess {
- // errLevel = platformapi.ErrLevelGeneralFail
- // err = utils.NewErrorCode(utils.Interface2String(result["err_code_desc"]), utils.Interface2String(result["err_code"]))
- // result = nil
- // } else {
- // }
}
}
return result, errLevel, err
@@ -281,7 +334,7 @@ func (a *API) OrderQuery(transactionID, outTradeNo string) (orderInfo *OrderInfo
TransactionID: transactionID,
OutTradeNo: outTradeNo,
}
- retVal, err := a.AccessAPI("orderquery", param)
+ retVal, err := a.AccessAPI("pay/orderquery", param)
if err == nil {
err = utils.Map2StructByJson(retVal, &orderInfo, false)
}
@@ -289,9 +342,20 @@ func (a *API) OrderQuery(transactionID, outTradeNo string) (orderInfo *OrderInfo
}
func (a *API) CreateUnifiedOrder(param *CreateOrderParam) (createOrderResult *CreateOrderResult, err error) {
- retVal, err := a.AccessAPI("unifiedorder", param)
+ retVal, err := a.AccessAPI("pay/unifiedorder", param)
if err == nil {
err = utils.Map2StructByJson(retVal, &createOrderResult, false)
}
return createOrderResult, err
}
+
+func (a *API) PayRefund(param *PayRefundParam) (refundResult *PayRefundResult, err error) {
+ if a.client.Transport == nil {
+ return nil, fmt.Errorf("没有配置证书,不能退款")
+ }
+ retVal, err := a.AccessAPI("secapi/pay/refund", param)
+ if err == nil {
+ err = utils.Map2StructByJson(retVal, &refundResult, false)
+ }
+ return refundResult, err
+}
diff --git a/platformapi/wxpay/wxpay_test.go b/platformapi/wxpay/wxpay_test.go
index d3a96222..834675f1 100644
--- a/platformapi/wxpay/wxpay_test.go
+++ b/platformapi/wxpay/wxpay_test.go
@@ -1,6 +1,7 @@
package wxpay
import (
+ "io/ioutil"
"strings"
"testing"
@@ -20,8 +21,10 @@ func init() {
logger, _ := zap.NewDevelopment()
sugarLogger = logger.Sugar()
baseapi.Init(sugarLogger)
-
- api = New("wx4b5930c13f8b1170", "XKJPOIHJ233adf01KJIXlIeQDSDKFJAD", "1390686702")
+ certPEMBlock, _ := ioutil.ReadFile("1390686702_20190115_cert/apiclient_cert.pem")
+ keyPEMBlock, _ := ioutil.ReadFile("1390686702_20190115_cert/apiclient_key.pem")
+ api = NewWithCertificate("wx4b5930c13f8b1170", "XKJPOIHJ233adf01KJIXlIeQDSDKFJAD", "1390686702", certPEMBlock, keyPEMBlock)
+ // api = New("wx4b5930c13f8b1170", "XKJPOIHJ233adf01KJIXlIeQDSDKFJAD", "1390686702")
}
func TestOrderQuery(t *testing.T) {
@@ -33,10 +36,12 @@ func TestOrderQuery(t *testing.T) {
}
func TestCreateUnifiedOrder(t *testing.T) {
+ orderNo := "367609100BA711EAAA20186590E02977" // utils.GetUUID()
+ // t.Log(orderNo)
result, err := api.CreateUnifiedOrder(&CreateOrderParam{
Body: "这里一个测试商品",
NotifyURL: "http://callback.test.jxc4.com/wxpay/msg/",
- OutTradeNo: utils.GetUUID(),
+ OutTradeNo: orderNo,
SpbillCreateIP: "114.114.114.114",
TradeType: TradeTypeNative,
TotalFee: 1,
@@ -47,6 +52,21 @@ func TestCreateUnifiedOrder(t *testing.T) {
t.Log(utils.Format4Output(result, false))
}
+func TestPayRefund(t *testing.T) {
+ result, err := api.PayRefund(&PayRefundParam{
+ TransactionID: "",
+ OutTradeNo: "8E0DD6260B7511EA908C186590E02977",
+ NotifyURL: "http://callback.test.jxc4.com/wxpay/msg/",
+ OutRefundNo: utils.GetUUID(),
+ TotalFee: 1,
+ RefundFee: 1,
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(utils.Format4Output(result, false))
+}
+
func TestXml2Json(t *testing.T) {
xmlStr := strings.Replace(`