Jerry 6 anni fa
parent
commit
0d6b47b8fe
13 ha cambiato i file con 168 aggiunte e 66 eliminazioni
  1. 4 0
      README.md
  2. 20 20
      alipay_client.go
  3. 10 10
      alipay_service_api.go
  4. 1 1
      constant.go
  5. 11 3
      examples/wechat/wx_ServiceApi.go
  6. 1 0
      go.mod
  7. 10 0
      go.sum
  8. 6 0
      release_note.txt
  9. 20 20
      wechat_client.go
  10. 11 0
      wechat_client_test.go
  11. BIN
      wechat_gopay.png
  12. 16 0
      wechat_rsp.go
  13. 58 12
      wechat_service_api.go

+ 4 - 0
README.md

@@ -56,6 +56,7 @@
 * gopay.GetOpenIdByAuthCode() => 授权码查询openid
 * gopay.GetAppWeChatLoginAccessToken() => App应用微信第三方登录,code换取access_token
 * gopay.RefreshAppWeChatLoginAccessToken() => 刷新App应用微信第三方登录后,获取的 access_token
+* gopay.DecryptRefundNotifyReqInfo() => 解密微信退款异步通知的加密数据
 
 ---
 
@@ -380,6 +381,9 @@ ok, err := gopay.VerifyWeChatSign(apiKey, gopay.SignType_MD5, notifyReq)
 //    返回参数 err:错误信息
 notifyReq, err := gopay.ParseWeChatRefundNotifyResult(c.Request())
 
+//==解密退款异步通知的加密参数 req_info ==
+refundNotify, err := gopay.DecryptRefundNotifyReqInfo(notifyReq.ReqInfo, apiKey)
+
 //==异步通知,返回给微信平台的信息==
 rsp := new(gopay.WeChatNotifyResponse) //回复微信的数据
 rsp.ReturnCode = gopay.SUCCESS

+ 20 - 20
alipay_client.go

@@ -55,7 +55,7 @@ func (a *AliPayClient) AliPayTradeFastPayRefundQuery(body BodyMap) (aliRsp *AliP
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -79,7 +79,7 @@ func (a *AliPayClient) AliPayTradeOrderSettle(body BodyMap) (aliRsp *AliPayTrade
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -103,7 +103,7 @@ func (a *AliPayClient) AliPayTradeCreate(body BodyMap) (aliRsp *AliPayTradeCreat
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -127,7 +127,7 @@ func (a *AliPayClient) AliPayTradeClose(body BodyMap) (aliRsp *AliPayTradeCloseR
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -151,7 +151,7 @@ func (a *AliPayClient) AliPayTradeCancel(body BodyMap) (aliRsp *AliPayTradeCance
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -175,7 +175,7 @@ func (a *AliPayClient) AliPayTradeRefund(body BodyMap) (aliRsp *AliPayTradeRefun
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -199,7 +199,7 @@ func (a *AliPayClient) AliPayTradePageRefund(body BodyMap) (aliRsp *AliPayTradeP
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -221,7 +221,7 @@ func (a *AliPayClient) AliPayTradePrecreate(body BodyMap) (aliRsp *AlipayTradePr
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -243,7 +243,7 @@ func (a *AliPayClient) AliPayTradePay(body BodyMap) (aliRsp *AliPayTradePayRespo
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -267,7 +267,7 @@ func (a *AliPayClient) AliPayTradeQuery(body BodyMap) (aliRsp *AliPayTradeQueryR
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -333,7 +333,7 @@ func (a *AliPayClient) AlipayFundTransToaccountTransfer(body BodyMap) (aliRsp *A
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -364,7 +364,7 @@ func (a *AliPayClient) AliPaySystemOauthToken(body BodyMap) (aliRsp *AliPaySyste
 	}
 	if aliRsp.Response.AccessToken == null {
 		info := aliRsp.ErrorResponse
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -384,7 +384,7 @@ func (a *AliPayClient) AlipayUserInfoShare() (aliRsp *AlipayUserInfoShareRespons
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -409,7 +409,7 @@ func (a *AliPayClient) AliPayOpenAuthTokenApp(body BodyMap) (aliRsp *AliPayOpenA
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -436,7 +436,7 @@ func (a *AliPayClient) ZhimaCreditScoreGet(body BodyMap) (aliRsp *ZhimaCreditSco
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -469,7 +469,7 @@ func (a *AliPayClient) AliPayUserCertifyOpenInit(body BodyMap) (aliRsp *AliPayUs
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -493,7 +493,7 @@ func (a *AliPayClient) AliPayUserCertifyOpenCertify(body BodyMap) (aliRsp *AliPa
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -517,7 +517,7 @@ func (a *AliPayClient) AliPayUserCertifyOpenQuery(body BodyMap) (aliRsp *AliPayU
 	}
 	if aliRsp.Response.Code != "10000" {
 		info := aliRsp.Response
-		return nil, fmt.Errorf(`{"code":"%v","msg":"%v","sub_code":"%v","sub_msg":"%v"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
+		return nil, fmt.Errorf(`{"code":"%s","msg":"%s","sub_code":"%s","sub_msg":"%s"}`, info.Code, info.Msg, info.SubCode, info.SubMsg)
 	}
 	aliRsp.SignData = getSignData(bs)
 	return
@@ -533,7 +533,7 @@ func (a *AliPayClient) doAliPay(body BodyMap, method string) (bytes []byte, err
 	)
 	if body != nil {
 		if bodyBs, err = json.Marshal(body); err != nil {
-			return nil, fmt.Errorf("json.Marshal:%v", err.Error())
+			return nil, fmt.Errorf("json.Marshal:%s", err.Error())
 		}
 		bodyStr = string(bodyBs)
 	}
@@ -599,7 +599,7 @@ func (a *AliPayClient) doAliPay(body BodyMap, method string) (bytes []byte, err
 		return nil, errs[0]
 	}
 	if res.StatusCode != 200 {
-		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %v", res.StatusCode)
+		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
 	}
 	if method == "alipay.trade.wap.pay" {
 		if res.Request.URL.String() == zfbSandboxBaseUrl || res.Request.URL.String() == zfbBaseUrl {

+ 10 - 10
alipay_service_api.go

@@ -70,7 +70,7 @@ func ParseAliPayNotifyResult(req *http.Request) (notifyReq *AliPayNotifyRequest,
 	if billList != null {
 		bills := make([]fundBillListInfo, 0)
 		if err = json.Unmarshal([]byte(billList), &bills); err != nil {
-			return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+			return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 		}
 		notifyReq.FundBillList = bills
 	} else {
@@ -81,7 +81,7 @@ func ParseAliPayNotifyResult(req *http.Request) (notifyReq *AliPayNotifyRequest,
 	if detailList != null {
 		details := make([]voucherDetailListInfo, 0)
 		if err = json.Unmarshal([]byte(detailList), &details); err != nil {
-			return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+			return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 		}
 		notifyReq.VoucherDetailList = details
 	} else {
@@ -133,11 +133,11 @@ func VerifyAliPaySign(aliPayPublicKey string, bean interface{}, syncSign ...stri
 		goto Verify
 	}
 	if bs, err = json.Marshal(bean); err != nil {
-		return false, fmt.Errorf("json.Marshal:%v", err.Error())
+		return false, fmt.Errorf("json.Marshal:%s", err.Error())
 	}
 	bm = make(BodyMap)
 	if err = json.Unmarshal(bs, &bm); err != nil {
-		return false, fmt.Errorf("json.Unmarshal:%v", err.Error())
+		return false, fmt.Errorf("json.Unmarshal:%s", err.Error())
 	}
 	bodySign = bm.Get("sign")
 	bodySignType = bm.Get("sign_type")
@@ -166,7 +166,7 @@ func verifyAliPaySign(signData, sign, signType, aliPayPublicKey string) (err err
 		return errors.New("支付宝公钥Decode错误")
 	}
 	if pubKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
-		return fmt.Errorf("x509.ParsePKIXPublicKey:%v", err.Error())
+		return fmt.Errorf("x509.ParsePKIXPublicKey:%s", err.Error())
 	}
 	if publicKey, ok = pubKey.(*rsa.PublicKey); !ok {
 		return errors.New("支付宝公钥转换错误")
@@ -309,7 +309,7 @@ func DecryptAliPayOpenDataToStruct(encryptedData, secretKey string, beanPtr inte
 	ivKey := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
 	secretData, _ := base64.StdEncoding.DecodeString(encryptedData)
 	if block, err = aes.NewCipher(aesKey); err != nil {
-		return fmt.Errorf("aes.NewCipher:%v", err.Error())
+		return fmt.Errorf("aes.NewCipher:%s", err.Error())
 	}
 	if len(secretData)%len(aesKey) != 0 {
 		return errors.New("encryptedData is error")
@@ -321,7 +321,7 @@ func DecryptAliPayOpenDataToStruct(encryptedData, secretKey string, beanPtr inte
 		originData = PKCS5UnPadding(originData)
 	}
 	if err = json.Unmarshal(originData, beanPtr); err != nil {
-		return fmt.Errorf("json.Unmarshal:%v", err.Error())
+		return fmt.Errorf("json.Unmarshal:%s", err.Error())
 	}
 	return nil
 }
@@ -341,7 +341,7 @@ func DecryptAliPayOpenDataToBodyMap(encryptedData, secretKey string) (bm BodyMap
 	aesKey, _ = base64.StdEncoding.DecodeString(secretKey)
 	secretData, _ := base64.StdEncoding.DecodeString(encryptedData)
 	if block, err = aes.NewCipher(aesKey); err != nil {
-		return nil, fmt.Errorf("aes.NewCipher:%v", err.Error())
+		return nil, fmt.Errorf("aes.NewCipher:%s", err.Error())
 	}
 	if len(secretData)%len(aesKey) != 0 {
 		return nil, errors.New("encryptedData is error")
@@ -354,7 +354,7 @@ func DecryptAliPayOpenDataToBodyMap(encryptedData, secretKey string) (bm BodyMap
 	}
 	bm = make(BodyMap)
 	if err = json.Unmarshal(originData, &bm); err != nil {
-		return nil, fmt.Errorf("json.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("json.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -383,7 +383,7 @@ func AliPaySystemOauthToken(appId, privateKey, grantType, codeOrToken string) (r
 	}
 	rsp = new(AliPaySystemOauthTokenResponse)
 	if err = json.Unmarshal(bs, rsp); err != nil {
-		return nil, fmt.Errorf("json.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("json.Unmarshal:%s", err.Error())
 	}
 	if rsp.Response.AccessToken == "" {
 		return nil, errors.New("access_token is null")

+ 1 - 1
constant.go

@@ -4,7 +4,7 @@ const (
 	null       string = ""
 	TimeLayout string = "2006-01-02 15:04:05"
 	DateLayout string = "2006-01-02"
-	Version    string = "1.4.2"
+	Version    string = "1.4.3"
 	//微信
 	//===========================================================================================
 

+ 11 - 3
examples/wechat/wx_ServiceApi.go

@@ -223,14 +223,22 @@ func ParseWeChatNotifyResultAndVerifyWeChatSign(req *http.Request) string {
 // 解析微信退款异步通知的参数,解析出来的 req_info 是加密数据,需解密
 func ParseWeChatRefundNotifyResult(req *http.Request) string {
 	rsp := new(gopay.WeChatNotifyResponse)
-	//解析参数
+	// 解析参数
 	notifyReq, err := gopay.ParseWeChatRefundNotifyResult(req)
 	if err != nil {
 		fmt.Println("err:", err)
 	}
 	fmt.Println("notifyReq:", *notifyReq)
-	//退款通知无sign,不用验签
-	//返回微信
+	// 退款通知无sign,不用验签
+
+	// 解密退款异步通知的加密数据
+	refundNotify, err := gopay.DecryptRefundNotifyReqInfo(notifyReq.ReqInfo, "GFDS8j98rewnmgl45wHTt980jg543abc")
+	if err != nil {
+		fmt.Println("err:", err)
+	}
+	fmt.Println("refundNotify:", *refundNotify)
+
+	// 返回微信
 	rsp.ReturnCode = gopay.SUCCESS
 	rsp.ReturnMsg = "OK"
 	return rsp.ToXmlString()

+ 1 - 0
go.mod

@@ -6,6 +6,7 @@ require (
 	github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 // indirect
 	github.com/parnurzeal/gorequest v0.2.16
 	github.com/pkg/errors v0.8.1 // indirect
+	github.com/smartystreets/goconvey v1.6.4 // indirect
 	golang.org/x/net v0.0.0-20191109021931-daa7c04131f5 // indirect
 	moul.io/http2curl v1.0.0 // indirect
 )

+ 10 - 0
go.sum

@@ -2,15 +2,25 @@ github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 h1:pEtiCjIXx3RvGjl
 github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
 github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
 github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ=
 github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20191109021931-daa7c04131f5 h1:bHNaocaoJxYBo5cw41UyTMLjYlb8wPY7+WFrnklbHOM=
 golang.org/x/net v0.0.0-20191109021931-daa7c04131f5/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
 moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=

+ 6 - 0
release_note.txt

@@ -1,3 +1,9 @@
+版本号:Release 1.4.3
+发布时间:2019/11/12 00:40
+修改记录:
+   (1) 微信:公共API方法:gopay.ParseWeChatRefundNotifyResult(),解析微信退款异步通知的请求参数
+   (2) 微信:公共API方法:gopay.DecryptRefundNotifyReqInfo(),解密微信退款异步通知的加密数据
+   (3) 修改部分小问题
 
 版本号:Release 1.4.2
 发布时间:2019/11/11 16:43

+ 20 - 20
wechat_client.go

@@ -50,7 +50,7 @@ func (w *WeChatClient) Micropay(body BodyMap) (wxRsp *WeChatMicropayResponse, er
 	}
 	wxRsp = new(WeChatMicropayResponse)
 	if err = xml.Unmarshal(bs, wxRsp); err != nil {
-		return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -70,7 +70,7 @@ func (w *WeChatClient) UnifiedOrder(body BodyMap) (wxRsp *WeChatUnifiedOrderResp
 	}
 	wxRsp = new(WeChatUnifiedOrderResponse)
 	if err = xml.Unmarshal(bs, wxRsp); err != nil {
-		return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -89,7 +89,7 @@ func (w *WeChatClient) QueryOrder(body BodyMap) (wxRsp *WeChatQueryOrderResponse
 	}
 	wxRsp = new(WeChatQueryOrderResponse)
 	if err = xml.Unmarshal(bs, wxRsp); err != nil {
-		return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -108,7 +108,7 @@ func (w *WeChatClient) CloseOrder(body BodyMap) (wxRsp *WeChatCloseOrderResponse
 	}
 	wxRsp = new(WeChatCloseOrderResponse)
 	if err = xml.Unmarshal(bs, wxRsp); err != nil {
-		return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -125,11 +125,11 @@ func (w *WeChatClient) Reverse(body BodyMap, certFilePath, keyFilePath, pkcs12Fi
 	if w.IsProd {
 		pkcsPool = x509.NewCertPool()
 		if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil {
-			return nil, fmt.Errorf("ioutil.ReadFile:%v", err.Error())
+			return nil, fmt.Errorf("ioutil.ReadFile:%s", err.Error())
 		}
 		pkcsPool.AppendCertsFromPEM(pkcs)
 		if certificate, err = tls.LoadX509KeyPair(certFilePath, keyFilePath); err != nil {
-			return nil, fmt.Errorf("tls.LoadX509KeyPair:%v", err.Error())
+			return nil, fmt.Errorf("tls.LoadX509KeyPair:%s", err.Error())
 		}
 		tlsConfig = &tls.Config{
 			Certificates:       []tls.Certificate{certificate},
@@ -144,7 +144,7 @@ func (w *WeChatClient) Reverse(body BodyMap, certFilePath, keyFilePath, pkcs12Fi
 	}
 	wxRsp = new(WeChatReverseResponse)
 	if err = xml.Unmarshal(bs, wxRsp); err != nil {
-		return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -161,11 +161,11 @@ func (w *WeChatClient) Refund(body BodyMap, certFilePath, keyFilePath, pkcs12Fil
 	if w.IsProd {
 		pkcsPool = x509.NewCertPool()
 		if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil {
-			return nil, fmt.Errorf("ioutil.ReadFile:%v", err.Error())
+			return nil, fmt.Errorf("ioutil.ReadFile:%s", err.Error())
 		}
 		pkcsPool.AppendCertsFromPEM(pkcs)
 		if certificate, err = tls.LoadX509KeyPair(certFilePath, keyFilePath); err != nil {
-			return nil, fmt.Errorf("tls.LoadX509KeyPair:%v", err.Error())
+			return nil, fmt.Errorf("tls.LoadX509KeyPair:%s", err.Error())
 		}
 		tlsConfig = &tls.Config{
 			Certificates:       []tls.Certificate{certificate},
@@ -180,7 +180,7 @@ func (w *WeChatClient) Refund(body BodyMap, certFilePath, keyFilePath, pkcs12Fil
 	}
 	wxRsp = new(WeChatRefundResponse)
 	if err = xml.Unmarshal(bs, wxRsp); err != nil {
-		return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -199,7 +199,7 @@ func (w *WeChatClient) QueryRefund(body BodyMap) (wxRsp *WeChatQueryRefundRespon
 	}
 	wxRsp = new(WeChatQueryRefundResponse)
 	if err = xml.Unmarshal(bs, wxRsp); err != nil {
-		return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -233,11 +233,11 @@ func (w *WeChatClient) DownloadFundFlow(body BodyMap, certFilePath, keyFilePath,
 	if w.IsProd {
 		pkcsPool = x509.NewCertPool()
 		if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil {
-			return null, fmt.Errorf("ioutil.ReadFile:%v", err.Error())
+			return null, fmt.Errorf("ioutil.ReadFile:%s", err.Error())
 		}
 		pkcsPool.AppendCertsFromPEM(pkcs)
 		if certificate, err = tls.LoadX509KeyPair(certFilePath, keyFilePath); err != nil {
-			return null, fmt.Errorf("tls.LoadX509KeyPair:%v", err.Error())
+			return null, fmt.Errorf("tls.LoadX509KeyPair:%s", err.Error())
 		}
 		tlsConfig = &tls.Config{
 			Certificates:       []tls.Certificate{certificate},
@@ -268,11 +268,11 @@ func (w *WeChatClient) BatchQueryComment(body BodyMap, certFilePath, keyFilePath
 		body.Set("sign_type", SignType_HMAC_SHA256)
 		pkcsPool = x509.NewCertPool()
 		if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil {
-			return null, fmt.Errorf("ioutil.ReadFile:%v", err.Error())
+			return null, fmt.Errorf("ioutil.ReadFile:%s", err.Error())
 		}
 		pkcsPool.AppendCertsFromPEM(pkcs)
 		if certificate, err = tls.LoadX509KeyPair(certFilePath, keyFilePath); err != nil {
-			return null, fmt.Errorf("tls.LoadX509KeyPair:%v", err.Error())
+			return null, fmt.Errorf("tls.LoadX509KeyPair:%s", err.Error())
 		}
 		tlsConfig = &tls.Config{
 			Certificates:       []tls.Certificate{certificate},
@@ -306,11 +306,11 @@ func (w *WeChatClient) Transfer(body BodyMap, certFilePath, keyFilePath, pkcs12F
 	)
 	pkcsPool = x509.NewCertPool()
 	if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil {
-		return nil, fmt.Errorf("ioutil.ReadFile:%v", err.Error())
+		return nil, fmt.Errorf("ioutil.ReadFile:%s", err.Error())
 	}
 	pkcsPool.AppendCertsFromPEM(pkcs)
 	if certificate, err = tls.LoadX509KeyPair(certFilePath, keyFilePath); err != nil {
-		return nil, fmt.Errorf("tls.LoadX509KeyPair:%v", err.Error())
+		return nil, fmt.Errorf("tls.LoadX509KeyPair:%s", err.Error())
 	}
 	tlsConfig = &tls.Config{
 		Certificates:       []tls.Certificate{certificate},
@@ -327,11 +327,11 @@ func (w *WeChatClient) Transfer(body BodyMap, certFilePath, keyFilePath, pkcs12F
 		return nil, errs[0]
 	}
 	if res.StatusCode != 200 {
-		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %v", res.StatusCode)
+		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
 	}
 	wxRsp = new(WeChatTransfersResponse)
 	if err = xml.Unmarshal(bs, wxRsp); err != nil {
-		return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -379,7 +379,7 @@ GoRequest:
 		return nil, errs[0]
 	}
 	if res.StatusCode != 200 {
-		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %v", res.StatusCode)
+		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
 	}
 	if strings.Contains(string(bytes), "HTML") {
 		return nil, errors.New(string(bytes))

+ 11 - 0
wechat_client_test.go

@@ -127,3 +127,14 @@ func TestDecryptWeChatOpenDataToBodyMap(t *testing.T) {
 	}
 	fmt.Println("WeChatUserPhone:", bm)
 }
+
+func TestDecryptRefundNotifyReqInfo(t *testing.T) {
+	key := "ziR0QKsTUfMOuochC9RfCdmfHECorQAP"
+	data := "YYwp8C48th0wnQzTqeI+41pflB26v+smFj9z6h9RPBgxTyZyxc+4YNEz7QEgZNWj/6rIb2MfyWMZmCc41CfjKSssoSZPXxOhUayb6KvNSZ1p6frOX1PDWzhyruXK7ouNND+gDsG4yZ0XXzsL4/pYNwLLba/71QrnkJ/BHcByk4EXnglju5DLup9pJQSnTxjomI9Rxu57m9jg5lLQFxMWXyeASZJNvof0ulnHlWJswS4OxKOkmW7VEyKyLGV6npoOm03Qsx2wkRxLsSa9gPpg4hdaReeUqh1FMbm7aWjyrVYT/MEZWg98p4GomEIYvz34XfDncTezX4bf/ZiSLXt79aE1/YTZrYfymXeCrGjlbe0rg/T2ezJHAC870u2vsVbY1/KcE2A443N+DEnAziXlBQ1AeWq3Rqk/O6/TMM0lomzgctAOiAMg+bh5+Gu1ubA9O3E+vehULydD5qx2o6i3+qA9ORbH415NyRrQdeFq5vmCiRikp5xYptWiGZA0tkoaLKMPQ4ndE5gWHqiBbGPfULZWokI+QjjhhBmwgbd6J0VqpRorwOuzC/BHdkP72DCdNcm7IDUpggnzBIy0+seWIkcHEryKjge3YDHpJeQCqrAH0CgxXHDt1xtbQbST1VqFyuhPhUjDXMXrknrGPN/oE1t0rLRq+78cI+k8xe5E6seeUXQsEe8r3358mpcDYSmXWSXVZxK6er9EF98APqHwcndyEJD2YyCh/mMVhERuX+7kjlRXSiNUWa/Cv/XAKFQuvUYA5ea2eYWtPRHa4DpyuF1SNsaqVKfgqKXZrJHfAgslVpSVqUpX4zkKszHF4kwMZO3M7J1P94Mxa7Tm9mTOJePOoHPXeEB+m9rX6pSfoi3mJDQ5inJ+Vc4gOkg/Wd/lqiy6TTyP/dHDN6/v+AuJx5AXBo/2NDD3fWhHjkqEKIuARr2ClZt9ZRQO4HkXdZo7CN06sGCHk48Tg8PmxnxKcMZm7Aoquv5yMIM2gWSWIRJhwJ8cUpafIHc+GesDlbF6Zbt+/KXkafJAQq2RklEN+WvZ/zFz113EPgWPjp16TwBoziq96MMekvWKY/vdhjol8VFtGH9F61Oy1Xwf6DJtPw=="
+	refundNotify, err := DecryptRefundNotifyReqInfo(data, key)
+	if err != nil {
+		fmt.Println("err:", err)
+		return
+	}
+	fmt.Println("refundNotify:", *refundNotify)
+}

BIN
wechat_gopay.png


+ 16 - 0
wechat_rsp.go

@@ -260,6 +260,22 @@ type WeChatRefundNotifyRequest struct {
 	ReqInfo    string `xml:"req_info,omitempty" json:"req_info,omitempty"`
 }
 
+type RefundNotify struct {
+	TransactionId       string `xml:"transaction_id,omitempty" json:"transaction_id,omitempty"`
+	OutTradeNo          string `xml:"out_trade_no,omitempty" json:"out_trade_no,omitempty"`
+	RefundId            string `xml:"refund_id,omitempty" json:"refund_id,omitempty"`
+	OutRefundNo         string `xml:"out_refund_no,omitempty" json:"out_refund_no,omitempty"`
+	TotalFee            int    `xml:"total_fee,omitempty" json:"total_fee,omitempty"`
+	SettlementTotalFee  int    `xml:"settlement_total_fee,omitempty" json:"settlement_total_fee,omitempty"`
+	RefundFee           int    `xml:"refund_fee,omitempty" json:"refund_fee,omitempty"`
+	SettlementRefundFee int    `xml:"settlement_refund_fee,omitempty" json:"settlement_refund_fee,omitempty"`
+	RefundStatus        string `xml:"refund_status,omitempty" json:"refund_status,omitempty"`
+	SuccessTime         string `xml:"success_time,omitempty" json:"success_time,omitempty"`
+	RefundRecvAccout    string `xml:"refund_recv_accout,omitempty" json:"refund_recv_accout,omitempty"`
+	RefundAccount       string `xml:"refund_account,omitempty" json:"refund_account,omitempty"`
+	RefundRequestSource string `xml:"refund_request_source,omitempty" json:"refund_request_source,omitempty"`
+}
+
 type Code2SessionRsp struct {
 	SessionKey string `json:"session_key,omitempty"` // 会话密钥
 	ExpiresIn  int    `json:"expires_in,omitempty"`  // SessionKey超时时间(秒)

+ 58 - 12
wechat_service_api.go

@@ -76,11 +76,11 @@ func GetWeChatSanBoxParamSign(appId, mchId, apiKey string, bm BodyMap) (sign str
 func ParseWeChatNotifyResultToBodyMap(req *http.Request) (bm BodyMap, err error) {
 	var bs []byte
 	if bs, err = ioutil.ReadAll(req.Body); err != nil {
-		return nil, fmt.Errorf("ioutil.ReadAll:%v", err.Error())
+		return nil, fmt.Errorf("ioutil.ReadAll:%s", err.Error())
 	}
 	bm = make(BodyMap)
 	if err = xml.Unmarshal(bs, &bm); err != nil {
-		return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -92,19 +92,65 @@ func ParseWeChatNotifyResultToBodyMap(req *http.Request) (bm BodyMap, err error)
 func ParseWeChatNotifyResult(req *http.Request) (notifyReq *WeChatNotifyRequest, err error) {
 	notifyReq = new(WeChatNotifyRequest)
 	if err = xml.NewDecoder(req.Body).Decode(notifyReq); err != nil {
-		return nil, fmt.Errorf("xml.NewDecoder:%v", err.Error())
+		return nil, fmt.Errorf("xml.NewDecoder:%s", err.Error())
 	}
 	return
 }
 
-// 解析微信退款异步通知的参数,解析出来的 req_info 是加密数据,需解密
+// 解析微信退款异步通知的参数
 //    req:*http.Request
 //    返回参数notifyReq:Notify请求的参数
 //    返回参数err:错误信息
 func ParseWeChatRefundNotifyResult(req *http.Request) (notifyReq *WeChatRefundNotifyRequest, err error) {
 	notifyReq = new(WeChatRefundNotifyRequest)
 	if err = xml.NewDecoder(req.Body).Decode(notifyReq); err != nil {
-		return nil, fmt.Errorf("xml.NewDecoder:%v", err.Error())
+		return nil, fmt.Errorf("xml.NewDecoder:%s", err.Error())
+	}
+	return
+}
+
+// 解密微信退款异步通知的加密数据
+//    reqInfo:gopay.ParseWeChatRefundNotifyResult() 方法获取的加密数据 req_info
+//    apiKey:API秘钥值
+//    返回参数refundNotify:RefundNotify请求的加密数据
+//    返回参数err:错误信息
+//    文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=10
+func DecryptRefundNotifyReqInfo(reqInfo, apiKey string) (refundNotify *RefundNotify, err error) {
+	var (
+		encryptionB, bs []byte
+		block           cipher.Block
+		blockSize       int
+	)
+	if encryptionB, err = base64.StdEncoding.DecodeString(reqInfo); err != nil {
+		return nil, err
+	}
+	h := md5.New()
+	h.Write([]byte(apiKey))
+	key := strings.ToLower(hex.EncodeToString(h.Sum(nil)))
+	if len(encryptionB)%aes.BlockSize != 0 {
+		return nil, errors.New("encryptedData is error")
+	}
+	if block, err = aes.NewCipher([]byte(key)); err != nil {
+		return nil, err
+	}
+	blockSize = block.BlockSize()
+	func(dst, src []byte) {
+		if len(src)%blockSize != 0 {
+			panic("crypto/cipher: input not full blocks")
+		}
+		if len(dst) < len(src) {
+			panic("crypto/cipher: output smaller than input")
+		}
+		for len(src) > 0 {
+			block.Decrypt(dst, src[:blockSize])
+			src = src[blockSize:]
+			dst = dst[blockSize:]
+		}
+	}(encryptionB, encryptionB)
+	bs = PKCS7UnPadding(encryptionB)
+	refundNotify = new(RefundNotify)
+	if err = xml.Unmarshal(bs, refundNotify); err != nil {
+		return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -131,11 +177,11 @@ func VerifyWeChatSign(apiKey, signType string, bean interface{}) (ok bool, err e
 		goto Verify
 	}
 	if bs, err = json.Marshal(bean); err != nil {
-		return false, fmt.Errorf("json.Marshal:%v", err.Error())
+		return false, fmt.Errorf("json.Marshal:%s", err.Error())
 	}
 	bm = make(BodyMap)
 	if err = json.Unmarshal(bs, &bm); err != nil {
-		return false, fmt.Errorf("json.Unmarshal:%v", err.Error())
+		return false, fmt.Errorf("json.Unmarshal:%s", err.Error())
 	}
 Verify:
 	bodySign = bm.Get("sign")
@@ -293,7 +339,7 @@ func DecryptWeChatOpenDataToStruct(encryptedData, iv, sessionKey string, beanPtr
 		return errors.New("encryptedData is error")
 	}
 	if block, err = aes.NewCipher(aesKey); err != nil {
-		return fmt.Errorf("aes.NewCipher:%v", err.Error())
+		return fmt.Errorf("aes.NewCipher:%s", err.Error())
 	}
 	blockMode = cipher.NewCBCDecrypter(block, ivKey)
 	plainText = make([]byte, len(cipherText))
@@ -302,7 +348,7 @@ func DecryptWeChatOpenDataToStruct(encryptedData, iv, sessionKey string, beanPtr
 		plainText = PKCS7UnPadding(plainText)
 	}
 	if err = json.Unmarshal(plainText, beanPtr); err != nil {
-		return fmt.Errorf("json.Unmarshal:%v", err.Error())
+		return fmt.Errorf("json.Unmarshal:%s", err.Error())
 	}
 	return
 }
@@ -325,7 +371,7 @@ func DecryptWeChatOpenDataToBodyMap(encryptedData, iv, sessionKey string) (bm Bo
 		return nil, errors.New("encryptedData is error")
 	}
 	if block, err = aes.NewCipher(aesKey); err != nil {
-		return nil, fmt.Errorf("aes.NewCipher:%v", err.Error())
+		return nil, fmt.Errorf("aes.NewCipher:%s", err.Error())
 	} else {
 		blockMode = cipher.NewCBCDecrypter(block, ivKey)
 		plainText = make([]byte, len(cipherText))
@@ -335,7 +381,7 @@ func DecryptWeChatOpenDataToBodyMap(encryptedData, iv, sessionKey string) (bm Bo
 		}
 		bm = make(BodyMap)
 		if err = json.Unmarshal(plainText, &bm); err != nil {
-			return nil, fmt.Errorf("json.Unmarshal:%v", err.Error())
+			return nil, fmt.Errorf("json.Unmarshal:%s", err.Error())
 		}
 		return
 	}
@@ -420,7 +466,7 @@ func GetOpenIdByAuthCode(appId, mchId, apiKey, authCode, nonceStr string) (openI
 	}
 	openIdRsp = new(OpenIdByAuthCodeRsp)
 	if err = xml.Unmarshal(bs, openIdRsp); err != nil {
-		return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
+		return nil, fmt.Errorf("xml.Unmarshal:%s", err.Error())
 	}
 	return
 }