diff --git a/constant.go b/constant.go index 428efa9b..54ed0f45 100644 --- a/constant.go +++ b/constant.go @@ -7,7 +7,7 @@ const ( OK = "OK" DebugOff = 0 DebugOn = 1 - Version = "1.5.25" + Version = "1.5.26" ) type DebugSwitch int8 diff --git a/release_note.txt b/release_note.txt index 49e4f559..38f65919 100644 --- a/release_note.txt +++ b/release_note.txt @@ -1,3 +1,9 @@ +版本号:Release 1.5.26 +发布时间:2021/01/29 19:38 +修改记录: + (1) 微信:重新整理文件分级,商户分账模块增加test方法说明 + (2) BodyMap: 去除 GetArrayBodyMap()、GetBodyMap() 方法 + 版本号:Release 1.5.25 发布时间:2020/12/31 18:38 修改记录: diff --git a/wechat/applet_api_test.go b/wechat/applet_api_test.go new file mode 100644 index 00000000..b1437b29 --- /dev/null +++ b/wechat/applet_api_test.go @@ -0,0 +1,68 @@ +package wechat + +import ( + "testing" + + "github.com/iGoogle-ink/gotil/xlog" +) + +func TestDecryptOpenDataToStruct(t *testing.T) { + data := "Kf3TdPbzEmhWMuPKtlKxIWDkijhn402w1bxoHL4kLdcKr6jT1jNcIhvDJfjXmJcgDWLjmBiIGJ5acUuSvxLws3WgAkERmtTuiCG10CKLsJiR+AXVk7B2TUQzsq88YVilDz/YAN3647REE7glGmeBPfvUmdbfDzhL9BzvEiuRhABuCYyTMz4iaM8hFjbLB1caaeoOlykYAFMWC5pZi9P8uw==" + iv := "Cds8j3VYoGvnTp1BrjXdJg==" + session := "lyY4HPQbaOYzZdG+JcYK9w==" + phone := new(UserPhone) + //解密开放数据 + // encryptedData:包括敏感数据在内的完整用户信息的加密数据 + // iv:加密算法的初始向量 + // sessionKey:会话密钥 + // beanPtr:需要解析到的结构体指针 + err := DecryptOpenDataToStruct(data, iv, session, phone) + if err != nil { + xlog.Error(err) + return + } + xlog.Debug("PhoneNumber:", phone.PhoneNumber) + xlog.Debug("PurePhoneNumber:", phone.PurePhoneNumber) + xlog.Debug("CountryCode:", phone.CountryCode) + xlog.Debug("Watermark:", phone.Watermark) + + sessionKey := "tiihtNczf5v6AKRyjwEUhQ==" + encryptedData := "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==" + iv2 := "r7BXXKkLb8qrSNn05n0qiA==" + + //微信小程序 用户信息 + userInfo := new(AppletUserInfo) + + err = DecryptOpenDataToStruct(encryptedData, iv2, sessionKey, userInfo) + if err != nil { + xlog.Error(err) + return + } + xlog.Debug("NickName:", userInfo.NickName) + xlog.Debug("AvatarUrl:", userInfo.AvatarUrl) + xlog.Debug("Country:", userInfo.Country) + xlog.Debug("Province:", userInfo.Province) + xlog.Debug("City:", userInfo.City) + xlog.Debug("Gender:", userInfo.Gender) + xlog.Debug("OpenId:", userInfo.OpenId) + xlog.Debug("UnionId:", userInfo.UnionId) + xlog.Debug("Watermark:", userInfo.Watermark) +} + +func TestGetAppletAccessToken(t *testing.T) { + token, err := GetAppletAccessToken("wxdaa2ab9ef87b5497", "AppSecret") + if err != nil { + xlog.Error(err) + return + } + xlog.Debug("token:", token) +} + +func TestCode2Session(t *testing.T) { + session, err := Code2Session("wx2e92b2ff5ed4db71", "AppSecret", "081XxRPj1e8Krp0uGUQj1s0MPj1XxRP5") + if err != nil { + xlog.Error(err) + return + } + xlog.Debug("Openid:", session.Openid) +} diff --git a/wechat/base_api.go b/wechat/base_api.go new file mode 100644 index 00000000..e3dd14a9 --- /dev/null +++ b/wechat/base_api.go @@ -0,0 +1,215 @@ +package wechat + +import ( + "crypto/tls" + "encoding/xml" + "errors" + "fmt" + + "github.com/iGoogle-ink/gopay" + "github.com/iGoogle-ink/gotil" +) + +// 统一下单 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter3_1.shtml +func (w *Client) UnifiedOrder(bm gopay.BodyMap) (wxRsp *UnifiedOrderResponse, err error) { + err = bm.CheckEmptyError("nonce_str", "body", "out_trade_no", "total_fee", "spbill_create_ip", "notify_url", "trade_type") + if err != nil { + return nil, err + } + var bs []byte + if w.IsProd { + bs, err = w.doProdPost(bm, unifiedOrder, nil) + } else { + bm.Set("total_fee", 101) + bs, err = w.doSanBoxPost(bm, sandboxUnifiedOrder) + } + if err != nil { + return nil, err + } + wxRsp = new(UnifiedOrderResponse) + if err = xml.Unmarshal(bs, wxRsp); err != nil { + return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) + } + return wxRsp, nil +} + +// 提交付款码支付 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter4_1.shtml +func (w *Client) Micropay(bm gopay.BodyMap) (wxRsp *MicropayResponse, err error) { + err = bm.CheckEmptyError("nonce_str", "body", "out_trade_no", "total_fee", "spbill_create_ip", "auth_code") + if err != nil { + return nil, err + } + var bs []byte + if w.IsProd { + bs, err = w.doProdPost(bm, microPay, nil) + } else { + bm.Set("total_fee", 1) + bs, err = w.doSanBoxPost(bm, sandboxMicroPay) + } + if err != nil { + return nil, err + } + wxRsp = new(MicropayResponse) + if err = xml.Unmarshal(bs, wxRsp); err != nil { + return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) + } + return wxRsp, nil +} + +// 查询订单 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter3_2.shtml +func (w *Client) QueryOrder(bm gopay.BodyMap) (wxRsp *QueryOrderResponse, resBm gopay.BodyMap, err error) { + err = bm.CheckEmptyError("nonce_str") + if err != nil { + return nil, nil, err + } + if bm.Get("out_trade_no") == gotil.NULL && bm.Get("transaction_id") == gotil.NULL { + return nil, nil, errors.New("out_trade_no and transaction_id are not allowed to be null at the same time") + } + var bs []byte + if w.IsProd { + bs, err = w.doProdPost(bm, orderQuery, nil) + } else { + bs, err = w.doSanBoxPost(bm, sandboxOrderQuery) + } + if err != nil { + return nil, nil, err + } + wxRsp = new(QueryOrderResponse) + if err = xml.Unmarshal(bs, wxRsp); err != nil { + return nil, nil, fmt.Errorf("xml.UnmarshalStruct(%s):%w", string(bs), err) + } + resBm = make(gopay.BodyMap) + if err = xml.Unmarshal(bs, &resBm); err != nil { + return nil, nil, fmt.Errorf("xml.UnmarshalBodyMap(%s):%w", string(bs), err) + } + return wxRsp, resBm, nil +} + +// 关闭订单 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter3_3.shtml +func (w *Client) CloseOrder(bm gopay.BodyMap) (wxRsp *CloseOrderResponse, err error) { + err = bm.CheckEmptyError("nonce_str", "out_trade_no") + if err != nil { + return nil, err + } + var bs []byte + if w.IsProd { + bs, err = w.doProdPost(bm, closeOrder, nil) + } else { + bs, err = w.doSanBoxPost(bm, sandboxCloseOrder) + } + if err != nil { + return nil, err + } + wxRsp = new(CloseOrderResponse) + if err = xml.Unmarshal(bs, wxRsp); err != nil { + return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) + } + return wxRsp, nil +} + +// 申请退款 +// 注意:如已使用client.AddCertFilePath()或client.AddCertFileContent()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传 nil,否则,3证书Path均不可空 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter3_4.shtml +func (w *Client) Refund(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12FilePath interface{}) (wxRsp *RefundResponse, resBm gopay.BodyMap, err error) { + if err = checkCertFilePathOrContent(certFilePath, keyFilePath, pkcs12FilePath); err != nil { + return nil, nil, err + } + err = bm.CheckEmptyError("nonce_str", "out_refund_no", "total_fee", "refund_fee") + if err != nil { + return nil, nil, err + } + if bm.Get("out_trade_no") == gotil.NULL && bm.Get("transaction_id") == gotil.NULL { + return nil, nil, errors.New("out_trade_no and transaction_id are not allowed to be null at the same time") + } + var ( + bs []byte + tlsConfig *tls.Config + ) + if w.IsProd { + if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil { + return nil, nil, err + } + bs, err = w.doProdPost(bm, refund, tlsConfig) + } else { + bs, err = w.doSanBoxPost(bm, sandboxRefund) + } + if err != nil { + return nil, nil, err + } + wxRsp = new(RefundResponse) + if err = xml.Unmarshal(bs, wxRsp); err != nil { + return nil, nil, fmt.Errorf("xml.UnmarshalStruct(%s):%w", string(bs), err) + } + resBm = make(gopay.BodyMap) + if err = xml.Unmarshal(bs, &resBm); err != nil { + return nil, nil, fmt.Errorf("xml.UnmarshalBodyMap(%s):%w", string(bs), err) + } + return wxRsp, resBm, nil +} + +// 查询退款 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter3_5.shtml +func (w *Client) QueryRefund(bm gopay.BodyMap) (wxRsp *QueryRefundResponse, resBm gopay.BodyMap, err error) { + err = bm.CheckEmptyError("nonce_str") + if err != nil { + return nil, nil, err + } + if bm.Get("refund_id") == gotil.NULL && bm.Get("out_refund_no") == gotil.NULL && bm.Get("transaction_id") == gotil.NULL && bm.Get("out_trade_no") == gotil.NULL { + return nil, nil, errors.New("refund_id, out_refund_no, out_trade_no, transaction_id are not allowed to be null at the same time") + } + var bs []byte + if w.IsProd { + bs, err = w.doProdPost(bm, refundQuery, nil) + } else { + bs, err = w.doSanBoxPost(bm, sandboxRefundQuery) + } + if err != nil { + return nil, nil, err + } + wxRsp = new(QueryRefundResponse) + if err = xml.Unmarshal(bs, wxRsp); err != nil { + return nil, nil, fmt.Errorf("xml.UnmarshalStruct(%s):%w", string(bs), err) + } + resBm = make(gopay.BodyMap) + if err = xml.Unmarshal(bs, &resBm); err != nil { + return nil, nil, fmt.Errorf("xml.UnmarshalBodyMap(%s):%w", string(bs), err) + } + return wxRsp, resBm, nil +} + +// 撤销订单 +// 注意:如已使用client.AddCertFilePath()或client.AddCertFileContent()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传 nil,否则,3证书Path均不可空 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter4_3.shtml +func (w *Client) Reverse(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12FilePath interface{}) (wxRsp *ReverseResponse, err error) { + if err = checkCertFilePathOrContent(certFilePath, keyFilePath, pkcs12FilePath); err != nil { + return nil, err + } + err = bm.CheckEmptyError("nonce_str", "out_trade_no") + if err != nil { + return nil, err + } + var ( + bs []byte + tlsConfig *tls.Config + ) + if w.IsProd { + if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil { + return nil, err + } + bs, err = w.doProdPost(bm, reverse, tlsConfig) + } else { + bs, err = w.doSanBoxPost(bm, sandboxReverse) + } + if err != nil { + return nil, err + } + wxRsp = new(ReverseResponse) + if err = xml.Unmarshal(bs, wxRsp); err != nil { + return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) + } + return wxRsp, nil +} diff --git a/wechat/base_api_test.go b/wechat/base_api_test.go new file mode 100644 index 00000000..d96ecf52 --- /dev/null +++ b/wechat/base_api_test.go @@ -0,0 +1,188 @@ +package wechat + +import ( + "strconv" + "testing" + "time" + + "github.com/iGoogle-ink/gopay" + "github.com/iGoogle-ink/gotil" + "github.com/iGoogle-ink/gotil/xlog" +) + +func TestClient_UnifiedOrder(t *testing.T) { + number := gotil.GetRandomString(32) + xlog.Info("out_trade_no:", number) + // 初始化参数Map + bm := make(gopay.BodyMap) + bm.Set("nonce_str", gotil.GetRandomString(32)). + Set("body", "H5支付"). + Set("out_trade_no", number). + Set("total_fee", 1). + Set("spbill_create_ip", "127.0.0.1"). + Set("notify_url", "http://www.gopay.ink"). + Set("trade_type", TradeType_H5). + Set("device_info", "WEB"). + Set("sign_type", SignType_MD5). + SetBodyMap("scene_info", func(bm gopay.BodyMap) { + bm.SetBodyMap("h5_info", func(bm gopay.BodyMap) { + bm.Set("type", "Wap") + bm.Set("wap_url", "http://www.gopay.ink") + bm.Set("wap_name", "H5测试支付") + }) + }) /*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ + + // 正式 + //sign := gopay.GetWeChatParamSign("wxdaa2ab9ef87b5497", "1368139502", "GFDS8j98rewnmgl45wHTt980jg543abc", body) + // 沙箱 + //sign, _ := gopay.GetWeChatSanBoxParamSign("wxdaa2ab9ef87b5497", "1368139502", "GFDS8j98rewnmgl45wHTt980jg543abc", body) + //body.Set("sign", sign) + + // 请求支付下单,成功后得到结果 + wxRsp, err := client.UnifiedOrder(bm) + if err != nil { + xlog.Errorf("client.UnifiedOrder(%+v),error:%+v", bm, err) + return + } + xlog.Info("wxRsp:", *wxRsp) + //xlog.Info("wxRsp.MwebUrl:", wxRsp.MwebUrl) + + timeStamp := strconv.FormatInt(time.Now().Unix(), 10) + + // 获取小程序支付需要的paySign + //pac := "prepay_id=" + wxRsp.PrepayId + //paySign := GetMiniPaySign(appId, wxRsp.NonceStr, pac, SignType_MD5, timeStamp, apiKey) + //xlog.Info("paySign:", paySign) + + // 获取H5支付需要的paySign + pac := "prepay_id=" + wxRsp.PrepayId + paySign := GetH5PaySign(appId, wxRsp.NonceStr, pac, SignType_MD5, timeStamp, apiKey) + xlog.Debug("paySign:", paySign) + + // 获取小程序需要的paySign + //paySign := GetAppPaySign(appId,"partnerid", wxRsp.NonceStr, wxRsp.PrepayId, SignType_MD5, timeStamp, apiKey) + //xlog.Info("paySign:", paySign) +} + +func TestClient_Micropay(t *testing.T) { + number := gotil.GetRandomString(32) + xlog.Info("out_trade_no:", number) + // 初始化参数Map + bm := make(gopay.BodyMap) + bm.Set("nonce_str", gotil.GetRandomString(32)). + Set("body", "扫用户付款码支付"). + Set("out_trade_no", number). + Set("total_fee", 1). + Set("spbill_create_ip", "127.0.0.1"). + Set("auth_code", "134622817080551492"). + Set("sign_type", SignType_MD5) + + // 请求支付,成功后得到结果 + wxRsp, err := client.Micropay(bm) + if err != nil { + xlog.Errorf("client.Micropay(%+v),error:%+v", bm, err) + return + } + xlog.Debug("Response:", *wxRsp) + ok, err := VerifySign(apiKey, SignType_MD5, wxRsp) + if err != nil { + xlog.Error(err) + } + xlog.Debug("同步验签结果:", ok) // 沙箱环境验签失败请用正式环境测 +} + +func TestClient_QueryOrder(t *testing.T) { + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", "MfZC2segKxh0bnJSELbvKNeH3d9oWvvQ"). + Set("nonce_str", gotil.GetRandomString(32)). + Set("sign_type", SignType_MD5) + + // 请求订单查询,成功后得到结果 + wxRsp, resBm, err := client.QueryOrder(bm) + if err != nil { + xlog.Errorf("client.QueryOrder(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", *wxRsp) + xlog.Debug("resBm:", resBm) +} + +func TestClient_CloseOrder(t *testing.T) { + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", "MfZC2segKxh0bnJSELbvKNeH3d9oWvvQ"). + Set("nonce_str", gotil.GetRandomString(32)). + Set("sign_type", SignType_MD5) + + // 请求关闭订单,成功后得到结果 + wxRsp, err := client.CloseOrder(bm) + if err != nil { + xlog.Errorf("client.CloseOrder(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", *wxRsp) +} + +func TestClient_Refund(t *testing.T) { + // 初始化参数结构体 + s := gotil.GetRandomString(64) + xlog.Info("out_refund_no:", s) + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", "QRcTBTbJLoDrWSW9FtpSFlgWhft2QbaY"). + Set("nonce_str", gotil.GetRandomString(32)). + Set("sign_type", SignType_MD5). + Set("out_refund_no", s). + Set("total_fee", 101). + Set("refund_fee", 101). + Set("notify_url", "https://www.gopay.ink") + + // 请求申请退款(沙箱环境下,证书路径参数可传空) + // body:参数Body + // certFilePath:cert证书路径 + // keyFilePath:Key证书路径 + // pkcs12FilePath:p12证书路径 + wxRsp, resBm, err := client.Refund(bm, nil, nil, nil) + if err != nil { + xlog.Errorf("client.Refund(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", *wxRsp) + xlog.Debug("resBm:", resBm) +} + +func TestClient_QueryRefund(t *testing.T) { + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", "97HiM5j6kGmM2fk7fYMc8MgKhPnEQ5Rk"). + Set("nonce_str", gotil.GetRandomString(32)). + Set("sign_type", SignType_MD5) /*. + Set("transaction_id", "97HiM5j6kGmM2fk7fYMc8MgKhPnEQ5Rk"). + Set("out_refund_no", "vk4264I1UQ3Hm3E4AKsavK8npylGSgQA092f9ckUxp8A2gXmnsLEdsupURVTcaC7"). + Set("refund_id", "97HiM5j6kGmM2fk7fYMc8MgKhPnEQ5Rk")*/ + + // 请求申请退款 + wxRsp, resBm, err := client.QueryRefund(bm) + if err != nil { + xlog.Errorf("client.QueryRefund(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", *wxRsp) + xlog.Debug("resBm:", resBm) +} + +func TestClient_Reverse(t *testing.T) { + // 初始化参数Map + bm := make(gopay.BodyMap) + bm.Set("nonce_str", gotil.GetRandomString(32)). + Set("out_trade_no", "6aDCor1nUcAihrV5JBlI09tLvXbUp02B"). + Set("sign_type", SignType_MD5) + + // 请求撤销订单,成功后得到结果,沙箱环境下,证书路径参数可传nil + wxRsp, err := client.Reverse(bm, nil, nil, nil) + if err != nil { + xlog.Errorf("client.Reverse(%+v),error:%+v", bm, err) + return + } + xlog.Debug("Response:", wxRsp) +} diff --git a/wechat/client.go b/wechat/client.go index 17beaac1..7fa84fff 100644 --- a/wechat/client.go +++ b/wechat/client.go @@ -49,32 +49,8 @@ func (w *Client) PostWeChatAPISelf(bm gopay.BodyMap, path string, tlsConfig *tls return w.doProdPost(bm, path, tlsConfig) } -// 提交付款码支付 -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1 -func (w *Client) Micropay(bm gopay.BodyMap) (wxRsp *MicropayResponse, err error) { - err = bm.CheckEmptyError("nonce_str", "body", "out_trade_no", "total_fee", "spbill_create_ip", "auth_code") - if err != nil { - return nil, err - } - var bs []byte - if w.IsProd { - bs, err = w.doProdPost(bm, microPay, nil) - } else { - bm.Set("total_fee", 1) - bs, err = w.doSanBoxPost(bm, sandboxMicroPay) - } - if err != nil { - return nil, err - } - wxRsp = new(MicropayResponse) - if err = xml.Unmarshal(bs, wxRsp); err != nil { - return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) - } - return wxRsp, nil -} - // 授权码查询openid(正式) -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_13&index=9 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter4_8.shtml func (w *Client) AuthCodeToOpenId(bm gopay.BodyMap) (wxRsp *AuthCodeToOpenIdResponse, err error) { err = bm.CheckEmptyError("nonce_str", "auth_code") if err != nil { @@ -92,188 +68,8 @@ func (w *Client) AuthCodeToOpenId(bm gopay.BodyMap) (wxRsp *AuthCodeToOpenIdResp return wxRsp, nil } -// 统一下单 -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 -func (w *Client) UnifiedOrder(bm gopay.BodyMap) (wxRsp *UnifiedOrderResponse, err error) { - err = bm.CheckEmptyError("nonce_str", "body", "out_trade_no", "total_fee", "spbill_create_ip", "notify_url", "trade_type") - if err != nil { - return nil, err - } - var bs []byte - if w.IsProd { - bs, err = w.doProdPost(bm, unifiedOrder, nil) - } else { - bm.Set("total_fee", 101) - bs, err = w.doSanBoxPost(bm, sandboxUnifiedOrder) - } - if err != nil { - return nil, err - } - wxRsp = new(UnifiedOrderResponse) - if err = xml.Unmarshal(bs, wxRsp); err != nil { - return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) - } - return wxRsp, nil -} - -// 查询订单 -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2 -func (w *Client) QueryOrder(bm gopay.BodyMap) (wxRsp *QueryOrderResponse, resBm gopay.BodyMap, err error) { - err = bm.CheckEmptyError("nonce_str") - if err != nil { - return nil, nil, err - } - if bm.Get("out_trade_no") == gotil.NULL && bm.Get("transaction_id") == gotil.NULL { - return nil, nil, errors.New("out_trade_no and transaction_id are not allowed to be null at the same time") - } - var bs []byte - if w.IsProd { - bs, err = w.doProdPost(bm, orderQuery, nil) - } else { - bs, err = w.doSanBoxPost(bm, sandboxOrderQuery) - } - if err != nil { - return nil, nil, err - } - wxRsp = new(QueryOrderResponse) - if err = xml.Unmarshal(bs, wxRsp); err != nil { - return nil, nil, fmt.Errorf("xml.UnmarshalStruct(%s):%w", string(bs), err) - } - resBm = make(gopay.BodyMap) - if err = xml.Unmarshal(bs, &resBm); err != nil { - return nil, nil, fmt.Errorf("xml.UnmarshalBodyMap(%s):%w", string(bs), err) - } - return wxRsp, resBm, nil -} - -// 关闭订单 -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3 -func (w *Client) CloseOrder(bm gopay.BodyMap) (wxRsp *CloseOrderResponse, err error) { - err = bm.CheckEmptyError("nonce_str", "out_trade_no") - if err != nil { - return nil, err - } - var bs []byte - if w.IsProd { - bs, err = w.doProdPost(bm, closeOrder, nil) - } else { - bs, err = w.doSanBoxPost(bm, sandboxCloseOrder) - } - if err != nil { - return nil, err - } - wxRsp = new(CloseOrderResponse) - if err = xml.Unmarshal(bs, wxRsp); err != nil { - return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) - } - return wxRsp, nil -} - -// 撤销订单 -// 注意:如已使用client.AddCertFilePath()或client.AddCertFileContent()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传 nil,否则,3证书Path均不可空 -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_11&index=3 -func (w *Client) Reverse(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12FilePath interface{}) (wxRsp *ReverseResponse, err error) { - if err = checkCertFilePathOrContent(certFilePath, keyFilePath, pkcs12FilePath); err != nil { - return nil, err - } - err = bm.CheckEmptyError("nonce_str", "out_trade_no") - if err != nil { - return nil, err - } - var ( - bs []byte - tlsConfig *tls.Config - ) - if w.IsProd { - if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil { - return nil, err - } - bs, err = w.doProdPost(bm, reverse, tlsConfig) - } else { - bs, err = w.doSanBoxPost(bm, sandboxReverse) - } - if err != nil { - return nil, err - } - wxRsp = new(ReverseResponse) - if err = xml.Unmarshal(bs, wxRsp); err != nil { - return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) - } - return wxRsp, nil -} - -// 申请退款 -// 注意:如已使用client.AddCertFilePath()或client.AddCertFileContent()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传 nil,否则,3证书Path均不可空 -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 -func (w *Client) Refund(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12FilePath interface{}) (wxRsp *RefundResponse, resBm gopay.BodyMap, err error) { - if err = checkCertFilePathOrContent(certFilePath, keyFilePath, pkcs12FilePath); err != nil { - return nil, nil, err - } - err = bm.CheckEmptyError("nonce_str", "out_refund_no", "total_fee", "refund_fee") - if err != nil { - return nil, nil, err - } - if bm.Get("out_trade_no") == gotil.NULL && bm.Get("transaction_id") == gotil.NULL { - return nil, nil, errors.New("out_trade_no and transaction_id are not allowed to be null at the same time") - } - var ( - bs []byte - tlsConfig *tls.Config - ) - if w.IsProd { - if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil { - return nil, nil, err - } - bs, err = w.doProdPost(bm, refund, tlsConfig) - } else { - bs, err = w.doSanBoxPost(bm, sandboxRefund) - } - if err != nil { - return nil, nil, err - } - wxRsp = new(RefundResponse) - if err = xml.Unmarshal(bs, wxRsp); err != nil { - return nil, nil, fmt.Errorf("xml.UnmarshalStruct(%s):%w", string(bs), err) - } - resBm = make(gopay.BodyMap) - if err = xml.Unmarshal(bs, &resBm); err != nil { - return nil, nil, fmt.Errorf("xml.UnmarshalBodyMap(%s):%w", string(bs), err) - } - return wxRsp, resBm, nil -} - -// 查询退款 -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5 -func (w *Client) QueryRefund(bm gopay.BodyMap) (wxRsp *QueryRefundResponse, resBm gopay.BodyMap, err error) { - err = bm.CheckEmptyError("nonce_str") - if err != nil { - return nil, nil, err - } - if bm.Get("refund_id") == gotil.NULL && bm.Get("out_refund_no") == gotil.NULL && bm.Get("transaction_id") == gotil.NULL && bm.Get("out_trade_no") == gotil.NULL { - return nil, nil, errors.New("refund_id, out_refund_no, out_trade_no, transaction_id are not allowed to be null at the same time") - } - var bs []byte - if w.IsProd { - bs, err = w.doProdPost(bm, refundQuery, nil) - } else { - bs, err = w.doSanBoxPost(bm, sandboxRefundQuery) - } - if err != nil { - return nil, nil, err - } - wxRsp = new(QueryRefundResponse) - if err = xml.Unmarshal(bs, wxRsp); err != nil { - return nil, nil, fmt.Errorf("xml.UnmarshalStruct(%s):%w", string(bs), err) - } - resBm = make(gopay.BodyMap) - if err = xml.Unmarshal(bs, &resBm); err != nil { - return nil, nil, fmt.Errorf("xml.UnmarshalBodyMap(%s):%w", string(bs), err) - } - return wxRsp, resBm, nil -} - // 下载对账单 -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_6 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter3_6.shtml func (w *Client) DownloadBill(bm gopay.BodyMap) (wxRsp string, err error) { err = bm.CheckEmptyError("nonce_str", "bill_date", "bill_type") if err != nil { @@ -298,7 +94,7 @@ func (w *Client) DownloadBill(bm gopay.BodyMap) (wxRsp string, err error) { // 下载资金账单(正式) // 注意:如已使用client.AddCertFilePath()或client.AddCertFileContent()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传 nil,否则,3证书Path均不可空 // 貌似不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名 -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_18&index=7 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter3_7.shtml func (w *Client) DownloadFundFlow(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12FilePath interface{}) (wxRsp string, err error) { if err = checkCertFilePathOrContent(certFilePath, keyFilePath, pkcs12FilePath); err != nil { return gotil.NULL, err @@ -325,12 +121,12 @@ func (w *Client) DownloadFundFlow(bm gopay.BodyMap, certFilePath, keyFilePath, p } // 交易保障 -// 文档地址:(JSAPI)https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_8&index=9 -// 文档地址:(付款码)https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_14&index=8 -// 文档地址:(Native)https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_8&index=9 -// 文档地址:(APP)https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_8&index=10 -// 文档地址:(H5)https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_8&index=9 -// 文档地址:(微信小程序)https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_8&index=9 +// 文档地址:(JSAPI)https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter3_9.shtml +// 文档地址:(付款码)https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter4_9.shtml +// 文档地址:(Native)https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter6_9.shtml +// 文档地址:(APP)https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter7_9.shtml +// 文档地址:(H5)https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter8_9.shtml +// 文档地址:(微信小程序)https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter5_9.shtml func (w *Client) Report(bm gopay.BodyMap) (wxRsp *ReportResponse, err error) { err = bm.CheckEmptyError("nonce_str", "interface_url", "execute_time", "return_code", "return_msg", "result_code", "user_ip") if err != nil { @@ -355,7 +151,7 @@ func (w *Client) Report(bm gopay.BodyMap) (wxRsp *ReportResponse, err error) { // 拉取订单评价数据(正式) // 注意:如已使用client.AddCertFilePath()或client.AddCertFileContent()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传 nil,否则,3证书Path均不可空 // 貌似不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名 -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_17&index=11 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/open/chapter3_11.shtml func (w *Client) BatchQueryComment(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12FilePath interface{}) (wxRsp string, err error) { if err = checkCertFilePathOrContent(certFilePath, keyFilePath, pkcs12FilePath); err != nil { return gotil.NULL, err @@ -376,81 +172,6 @@ func (w *Client) BatchQueryComment(bm gopay.BodyMap, certFilePath, keyFilePath, return string(bs), nil } -// 公众号纯签约(正式) -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_1&index=1 -func (w *Client) EntrustPublic(bm gopay.BodyMap) (wxRsp *EntrustPublicResponse, err error) { - err = bm.CheckEmptyError("plan_id", "contract_code", "request_serial", "contract_display_account", "notify_url", "version", "timestamp") - if err != nil { - return nil, err - } - bs, err := w.doProdGet(bm, entrustPublic, SignType_MD5) - if err != nil { - return nil, err - } - wxRsp = new(EntrustPublicResponse) - if err = xml.Unmarshal(bs, wxRsp); err != nil { - return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) - } - return wxRsp, nil -} - -// APP纯签约-预签约接口-获取预签约ID(正式) -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_5&index=2 -func (w *Client) EntrustAppPre(bm gopay.BodyMap) (wxRsp *EntrustAppPreResponse, err error) { - err = bm.CheckEmptyError("plan_id", "contract_code", "request_serial", "contract_display_account", "notify_url", "version", "timestamp") - if err != nil { - return nil, err - } - bs, err := w.doProdPost(bm, entrustApp, nil) - if err != nil { - return nil, err - } - wxRsp = new(EntrustAppPreResponse) - if err = xml.Unmarshal(bs, wxRsp); err != nil { - return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) - } - return wxRsp, nil -} - -// H5纯签约(正式) -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_16&index=4 -func (w *Client) EntrustH5(bm gopay.BodyMap) (wxRsp *EntrustH5Response, err error) { - err = bm.CheckEmptyError("plan_id", "contract_code", "request_serial", "contract_display_account", "notify_url", "version", "timestamp", "clientip") - if err != nil { - return nil, err - } - bs, err := w.doProdGet(bm, entrustH5, SignType_HMAC_SHA256) - if err != nil { - return nil, err - } - wxRsp = new(EntrustH5Response) - if err = xml.Unmarshal(bs, wxRsp); err != nil { - return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) - } - return wxRsp, nil -} - -// 支付中签约(正式) -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_13&index=5 -func (w *Client) EntrustPaying(bm gopay.BodyMap) (wxRsp *EntrustPayingResponse, err error) { - err = bm.CheckEmptyError("contract_mchid", "contract_appid", - "out_trade_no", "nonce_str", "body", "notify_url", "total_fee", - "spbill_create_ip", "trade_type", "plan_id", "contract_code", - "request_serial", "contract_display_account", "contract_notify_url") - if err != nil { - return nil, err - } - bs, err := w.doProdPost(bm, entrustPaying, nil) - if err != nil { - return nil, err - } - wxRsp = new(EntrustPayingResponse) - if err = xml.Unmarshal(bs, wxRsp); err != nil { - return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) - } - return wxRsp, nil -} - // doSanBoxPost sanbox环境post请求 func (w *Client) doSanBoxPost(bm gopay.BodyMap, path string) (bs []byte, err error) { var url = baseUrlCh + path @@ -471,11 +192,11 @@ func (w *Client) doSanBoxPost(bm gopay.BodyMap, path string) (bs []byte, err err if w.BaseURL != gotil.NULL { url = w.BaseURL + path } + req := GenerateXml(bm) if w.DebugSwitch == gopay.DebugOn { - req, _ := json.Marshal(bm) xlog.Debugf("Wechat_Request: %s", req) } - res, bs, errs := xhttp.NewClient().Type(xhttp.TypeXML).Post(url).SendString(generateXml(bm)).EndBytes() + res, bs, errs := xhttp.NewClient().Type(xhttp.TypeXML).Post(url).SendString(req).EndBytes() if len(errs) > 0 { return nil, errs[0] } @@ -515,11 +236,11 @@ func (w *Client) doProdPost(bm gopay.BodyMap, path string, tlsConfig *tls.Config if w.BaseURL != gotil.NULL { url = w.BaseURL + path } + req := GenerateXml(bm) if w.DebugSwitch == gopay.DebugOn { - req, _ := json.Marshal(bm) xlog.Debugf("Wechat_Request: %s", req) } - res, bs, errs := httpClient.Type(xhttp.TypeXML).Post(url).SendString(generateXml(bm)).EndBytes() + res, bs, errs := httpClient.Type(xhttp.TypeXML).Post(url).SendString(req).EndBytes() if len(errs) > 0 { return nil, errs[0] } @@ -544,11 +265,11 @@ func (w *Client) doProdPostPure(bm gopay.BodyMap, path string, tlsConfig *tls.Co if w.BaseURL != gotil.NULL { url = w.BaseURL + path } + req := GenerateXml(bm) if w.DebugSwitch == gopay.DebugOn { - req, _ := json.Marshal(bm) xlog.Debugf("Wechat_Request: %s", req) } - res, bs, errs := httpClient.Type(xhttp.TypeXML).Post(url).SendString(generateXml(bm)).EndBytes() + res, bs, errs := httpClient.Type(xhttp.TypeXML).Post(url).SendString(req).EndBytes() if len(errs) > 0 { return nil, errs[0] } diff --git a/wechat/client_test.go b/wechat/client_test.go index 15c793be..a825b7f7 100644 --- a/wechat/client_test.go +++ b/wechat/client_test.go @@ -2,14 +2,11 @@ package wechat import ( "os" - "strconv" "testing" - "time" "github.com/iGoogle-ink/gopay" "github.com/iGoogle-ink/gotil" "github.com/iGoogle-ink/gotil/xlog" - "github.com/iGoogle-ink/gotil/xrsa" ) var ( @@ -49,120 +46,6 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestClient_UnifiedOrder(t *testing.T) { - number := gotil.GetRandomString(32) - xlog.Info("out_trade_no:", number) - // 初始化参数Map - bm := make(gopay.BodyMap) - bm.Set("nonce_str", gotil.GetRandomString(32)). - Set("body", "H5支付"). - Set("out_trade_no", number). - Set("total_fee", 1). - Set("spbill_create_ip", "127.0.0.1"). - Set("notify_url", "http://www.gopay.ink"). - Set("trade_type", TradeType_H5). - Set("device_info", "WEB"). - Set("sign_type", SignType_MD5). - SetBodyMap("scene_info", func(bm gopay.BodyMap) { - bm.SetBodyMap("h5_info", func(bm gopay.BodyMap) { - bm.Set("type", "Wap") - bm.Set("wap_url", "http://www.gopay.ink") - bm.Set("wap_name", "H5测试支付") - }) - }) /*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ - - // 正式 - //sign := gopay.GetWeChatParamSign("wxdaa2ab9ef87b5497", "1368139502", "GFDS8j98rewnmgl45wHTt980jg543abc", body) - // 沙箱 - //sign, _ := gopay.GetWeChatSanBoxParamSign("wxdaa2ab9ef87b5497", "1368139502", "GFDS8j98rewnmgl45wHTt980jg543abc", body) - //body.Set("sign", sign) - - // 请求支付下单,成功后得到结果 - wxRsp, err := client.UnifiedOrder(bm) - if err != nil { - xlog.Errorf("client.UnifiedOrder(%+v),error:%+v", bm, err) - return - } - xlog.Info("wxRsp:", *wxRsp) - //xlog.Info("wxRsp.MwebUrl:", wxRsp.MwebUrl) - - timeStamp := strconv.FormatInt(time.Now().Unix(), 10) - - // 获取小程序支付需要的paySign - //pac := "prepay_id=" + wxRsp.PrepayId - //paySign := GetMiniPaySign(appId, wxRsp.NonceStr, pac, SignType_MD5, timeStamp, apiKey) - //xlog.Info("paySign:", paySign) - - // 获取H5支付需要的paySign - pac := "prepay_id=" + wxRsp.PrepayId - paySign := GetH5PaySign(appId, wxRsp.NonceStr, pac, SignType_MD5, timeStamp, apiKey) - xlog.Debug("paySign:", paySign) - - // 获取小程序需要的paySign - //paySign := GetAppPaySign(appId,"partnerid", wxRsp.NonceStr, wxRsp.PrepayId, SignType_MD5, timeStamp, apiKey) - //xlog.Info("paySign:", paySign) -} - -func TestClient_QueryOrder(t *testing.T) { - // 初始化参数结构体 - bm := make(gopay.BodyMap) - bm.Set("out_trade_no", "MfZC2segKxh0bnJSELbvKNeH3d9oWvvQ"). - Set("nonce_str", gotil.GetRandomString(32)). - Set("sign_type", SignType_MD5) - - // 请求订单查询,成功后得到结果 - wxRsp, resBm, err := client.QueryOrder(bm) - if err != nil { - xlog.Errorf("client.QueryOrder(%+v),error:%+v", bm, err) - return - } - xlog.Debug("wxRsp:", *wxRsp) - xlog.Debug("resBm:", resBm) -} - -func TestClient_CloseOrder(t *testing.T) { - // 初始化参数结构体 - bm := make(gopay.BodyMap) - bm.Set("out_trade_no", "MfZC2segKxh0bnJSELbvKNeH3d9oWvvQ"). - Set("nonce_str", gotil.GetRandomString(32)). - Set("sign_type", SignType_MD5) - - // 请求关闭订单,成功后得到结果 - wxRsp, err := client.CloseOrder(bm) - if err != nil { - xlog.Errorf("client.CloseOrder(%+v),error:%+v", bm, err) - return - } - xlog.Debug("wxRsp:", *wxRsp) -} - -func TestClient_Micropay(t *testing.T) { - number := gotil.GetRandomString(32) - xlog.Info("out_trade_no:", number) - // 初始化参数Map - bm := make(gopay.BodyMap) - bm.Set("nonce_str", gotil.GetRandomString(32)). - Set("body", "扫用户付款码支付"). - Set("out_trade_no", number). - Set("total_fee", 1). - Set("spbill_create_ip", "127.0.0.1"). - Set("auth_code", "134622817080551492"). - Set("sign_type", SignType_MD5) - - // 请求支付,成功后得到结果 - wxRsp, err := client.Micropay(bm) - if err != nil { - xlog.Errorf("client.Micropay(%+v),error:%+v", bm, err) - return - } - xlog.Debug("Response:", *wxRsp) - ok, err := VerifySign(apiKey, SignType_MD5, wxRsp) - if err != nil { - xlog.Error(err) - } - xlog.Debug("同步验签结果:", ok) // 沙箱环境验签失败请用正式环境测 -} - func TestClient_AuthCodeToOpenId(t *testing.T) { // 初始化参数Map bm := make(gopay.BodyMap) @@ -177,94 +60,6 @@ func TestClient_AuthCodeToOpenId(t *testing.T) { xlog.Debug("Response:", *wxRsp) } -func TestClient_Refund(t *testing.T) { - // 初始化参数结构体 - s := gotil.GetRandomString(64) - xlog.Info("out_refund_no:", s) - bm := make(gopay.BodyMap) - bm.Set("out_trade_no", "QRcTBTbJLoDrWSW9FtpSFlgWhft2QbaY"). - Set("nonce_str", gotil.GetRandomString(32)). - Set("sign_type", SignType_MD5). - Set("out_refund_no", s). - Set("total_fee", 101). - Set("refund_fee", 101). - Set("notify_url", "https://www.gopay.ink") - - // 请求申请退款(沙箱环境下,证书路径参数可传空) - // body:参数Body - // certFilePath:cert证书路径 - // keyFilePath:Key证书路径 - // pkcs12FilePath:p12证书路径 - wxRsp, resBm, err := client.Refund(bm, nil, nil, nil) - if err != nil { - xlog.Errorf("client.Refund(%+v),error:%+v", bm, err) - return - } - xlog.Debug("wxRsp:", *wxRsp) - xlog.Debug("resBm:", resBm) -} - -func TestClient_QueryRefund(t *testing.T) { - // 初始化参数结构体 - bm := make(gopay.BodyMap) - bm.Set("out_trade_no", "97HiM5j6kGmM2fk7fYMc8MgKhPnEQ5Rk"). - Set("nonce_str", gotil.GetRandomString(32)). - Set("sign_type", SignType_MD5) /*. - Set("transaction_id", "97HiM5j6kGmM2fk7fYMc8MgKhPnEQ5Rk"). - Set("out_refund_no", "vk4264I1UQ3Hm3E4AKsavK8npylGSgQA092f9ckUxp8A2gXmnsLEdsupURVTcaC7"). - Set("refund_id", "97HiM5j6kGmM2fk7fYMc8MgKhPnEQ5Rk")*/ - - // 请求申请退款 - wxRsp, resBm, err := client.QueryRefund(bm) - if err != nil { - xlog.Errorf("client.QueryRefund(%+v),error:%+v", bm, err) - return - } - xlog.Debug("wxRsp:", *wxRsp) - xlog.Debug("resBm:", resBm) -} - -func TestClient_Reverse(t *testing.T) { - // 初始化参数Map - bm := make(gopay.BodyMap) - bm.Set("nonce_str", gotil.GetRandomString(32)). - Set("out_trade_no", "6aDCor1nUcAihrV5JBlI09tLvXbUp02B"). - Set("sign_type", SignType_MD5) - - // 请求撤销订单,成功后得到结果,沙箱环境下,证书路径参数可传nil - wxRsp, err := client.Reverse(bm, nil, nil, nil) - if err != nil { - xlog.Errorf("client.Reverse(%+v),error:%+v", bm, err) - return - } - xlog.Debug("Response:", wxRsp) -} - -func TestClient_Transfer(t *testing.T) { - // 初始化参数结构体 - bm := make(gopay.BodyMap) - bm.Set("nonce_str", gotil.GetRandomString(32)). - Set("partner_trade_no", gotil.GetRandomString(32)). - Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8"). - Set("check_name", "FORCE_CHECK"). // NO_CHECK:不校验真实姓名 , FORCE_CHECK:强校验真实姓名 - Set("re_user_name", "付明明"). // 收款用户真实姓名。 如果check_name设置为FORCE_CHECK,则必填用户真实姓名 - Set("amount", 30). // 企业付款金额,单位为分 - Set("desc", "测试转账"). // 企业付款备注,必填。注意:备注中的敏感词会被转成字符* - Set("spbill_create_ip", "127.0.0.1") - - // 企业向微信用户个人付款(不支持沙箱环境) - // body:参数Body - // certFilePath:cert证书路径 - // keyFilePath:Key证书路径 - // pkcs12FilePath:p12证书路径 - wxRsp, err := client.Transfer(bm, nil, nil, nil) - if err != nil { - xlog.Errorf("client.Transfer(%+v),error:%+v", bm, err) - return - } - xlog.Debug("wxRsp:", *wxRsp) -} - func TestClient_GetTransferInfo(t *testing.T) { // 初始化参数结构体 bm := make(gopay.BodyMap) @@ -335,216 +130,3 @@ func TestClient_BatchQueryComment(t *testing.T) { } xlog.Debug("wxRsp:", wxRsp) } - -func TestClient_EntrustPublic(t *testing.T) { - // 初始化参数结构体 - bm := make(gopay.BodyMap) - bm.Set("plan_id", "12535"). - Set("contract_code", "100000"). - Set("request_serial", "1000"). - Set("contract_display_account", "微信代扣"). - Set("notify_url", "https://www.igoogle.ink"). - Set("version", "1.0"). - Set("timestamp", time.Now().Unix()) - - // 公众号纯签约 - wxRsp, err := client.EntrustPublic(bm) - if err != nil { - xlog.Errorf("client.EntrustPublic(%+v),error:%+v", bm, err) - return - } - xlog.Debug("wxRsp:", wxRsp) -} - -func TestClient_EntrustAppPre(t *testing.T) { - // 初始化参数结构体 - bm := make(gopay.BodyMap) - bm.Set("plan_id", "12535"). - Set("contract_code", "100000"). - Set("request_serial", "1000"). - Set("contract_display_account", "微信代扣"). - Set("notify_url", "https://www.igoogle.ink"). - Set("version", "1.0"). - Set("timestamp", time.Now().Unix()) - - // APP纯签约 - wxRsp, err := client.EntrustAppPre(bm) - if err != nil { - xlog.Errorf("client.EntrustAppPre(%+v),error:%+v", bm, err) - return - } - xlog.Debug("wxRsp:", wxRsp) -} - -func TestClient_EntrustH5(t *testing.T) { - // 初始化参数结构体 - bm := make(gopay.BodyMap) - bm.Set("plan_id", "12535"). - Set("contract_code", "100000"). - Set("request_serial", "1000"). - Set("contract_display_account", "微信代扣"). - Set("notify_url", "https://www.igoogle.ink"). - Set("version", "1.0"). - Set("timestamp", time.Now().Unix()). - Set("clientip", "127.0.0.1") - - // H5纯签约 - wxRsp, err := client.EntrustH5(bm) - if err != nil { - xlog.Errorf("client.EntrustH5(%+v),error:%+v", bm, err) - return - } - xlog.Debug("wxRsp:", wxRsp) -} - -func TestClient_EntrustPaying(t *testing.T) { - number := gotil.GetRandomString(32) - xlog.Info("out_trade_no:", number) - // 初始化参数结构体 - bm := make(gopay.BodyMap) - bm.Set("contract_mchid", mchId). - Set("contract_appid", appId). - Set("out_trade_no", number). - Set("nonce_str", gotil.GetRandomString(32)). - Set("body", "测试签约"). - Set("total_fee", 1). - Set("spbill_create_ip", "127.0.0.1"). - Set("trade_type", TradeType_App). - Set("plan_id", "12535"). - Set("contract_code", "100000"). - Set("request_serial", "1000"). - Set("contract_display_account", "微信代扣"). - Set("notify_url", "https://www.igoogle.ink"). - Set("contract_notify_url", "https://www.igoogle.ink") - - //bm.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8") - - // 支付中签约 - wxRsp, err := client.EntrustPaying(bm) - if err != nil { - xlog.Errorf("client.EntrustPaying(%+v),error:%+v", bm, err) - return - } - xlog.Debug("wxRsp:", wxRsp) -} - -func TestClient_PayBank(t *testing.T) { - // 初始化参数结构体 - bm := make(gopay.BodyMap) - bm.Set("partner_trade_no", mchId). - Set("nonce_str", gotil.GetRandomString(32)). - Set("bank_code", "1001"). // 招商银行,https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_4&index=5 - Set("amount", 1) - - encryptBank, err := xrsa.RsaEncryptDataV2(xrsa.PKCS1, []byte("621400000000567"), "publicKey.pem") - if err != nil { - xlog.Error(err) - return - } - encryptName, err := xrsa.RsaEncryptDataV2(xrsa.PKCS1, []byte("Jerry"), "publicKey.pem") - if err != nil { - xlog.Error(err) - return - } - bm.Set("enc_bank_no", encryptBank). - Set("enc_true_name", encryptName) - - // 企业付款到银行卡API - wxRsp, err := client.PayBank(bm, "certFilePath", "keyFilePath", "pkcs12FilePath") - if err != nil { - xlog.Errorf("client.EntrustPaying(%+v),error:%+v", bm, err) - return - } - xlog.Debug("wxRsp:", wxRsp) -} - -// ======================= - -func TestDecryptOpenDataToStruct(t *testing.T) { - data := "Kf3TdPbzEmhWMuPKtlKxIWDkijhn402w1bxoHL4kLdcKr6jT1jNcIhvDJfjXmJcgDWLjmBiIGJ5acUuSvxLws3WgAkERmtTuiCG10CKLsJiR+AXVk7B2TUQzsq88YVilDz/YAN3647REE7glGmeBPfvUmdbfDzhL9BzvEiuRhABuCYyTMz4iaM8hFjbLB1caaeoOlykYAFMWC5pZi9P8uw==" - iv := "Cds8j3VYoGvnTp1BrjXdJg==" - session := "lyY4HPQbaOYzZdG+JcYK9w==" - phone := new(UserPhone) - //解密开放数据 - // encryptedData:包括敏感数据在内的完整用户信息的加密数据 - // iv:加密算法的初始向量 - // sessionKey:会话密钥 - // beanPtr:需要解析到的结构体指针 - err := DecryptOpenDataToStruct(data, iv, session, phone) - if err != nil { - xlog.Error(err) - return - } - xlog.Debug("PhoneNumber:", phone.PhoneNumber) - xlog.Debug("PurePhoneNumber:", phone.PurePhoneNumber) - xlog.Debug("CountryCode:", phone.CountryCode) - xlog.Debug("Watermark:", phone.Watermark) - - sessionKey := "tiihtNczf5v6AKRyjwEUhQ==" - encryptedData := "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==" - iv2 := "r7BXXKkLb8qrSNn05n0qiA==" - - //微信小程序 用户信息 - userInfo := new(AppletUserInfo) - - err = DecryptOpenDataToStruct(encryptedData, iv2, sessionKey, userInfo) - if err != nil { - xlog.Error(err) - return - } - xlog.Debug("NickName:", userInfo.NickName) - xlog.Debug("AvatarUrl:", userInfo.AvatarUrl) - xlog.Debug("Country:", userInfo.Country) - xlog.Debug("Province:", userInfo.Province) - xlog.Debug("City:", userInfo.City) - xlog.Debug("Gender:", userInfo.Gender) - xlog.Debug("OpenId:", userInfo.OpenId) - xlog.Debug("UnionId:", userInfo.UnionId) - xlog.Debug("Watermark:", userInfo.Watermark) -} - -func TestDecryptOpenDataToBodyMap(t *testing.T) { - data := "Kf3TdPbzEmhWMuPKtlKxIWDkijhn402w1bxoHL4kLdcKr6jT1jNcIhvDJfjXmJcgDWLjmBiIGJ5acUuSvxLws3WgAkERmtTuiCG10CKLsJiR+AXVk7B2TUQzsq88YVilDz/YAN3647REE7glGmeBPfvUmdbfDzhL9BzvEiuRhABuCYyTMz4iaM8hFjbLB1caaeoOlykYAFMWC5pZi9P8uw==" - iv := "Cds8j3VYoGvnTp1BrjXdJg==" - session := "lyY4HPQbaOYzZdG+JcYK9w==" - - //解密开放数据 - // encryptedData:包括敏感数据在内的完整用户信息的加密数据 - // iv:加密算法的初始向量 - // sessionKey:会话密钥 - bm, err := DecryptOpenDataToBodyMap(data, iv, session) - if err != nil { - xlog.Error(err) - return - } - xlog.Debug("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 { - xlog.Error(err) - return - } - xlog.Debug("refundNotify:", *refundNotify) -} - -func TestGetAppletAccessToken(t *testing.T) { - token, err := GetAppletAccessToken("wxdaa2ab9ef87b5497", "AppSecret") - if err != nil { - xlog.Error(err) - return - } - xlog.Debug("token:", token) -} - -func TestCode2Session(t *testing.T) { - session, err := Code2Session("wx2e92b2ff5ed4db71", "AppSecret", "081XxRPj1e8Krp0uGUQj1s0MPj1XxRP5") - if err != nil { - xlog.Error(err) - return - } - xlog.Debug("Openid:", session.Openid) -} diff --git a/wechat/entrust.go b/wechat/entrust.go new file mode 100644 index 00000000..4468b8f2 --- /dev/null +++ b/wechat/entrust.go @@ -0,0 +1,83 @@ +package wechat + +import ( + "encoding/xml" + "fmt" + + "github.com/iGoogle-ink/gopay" +) + +// 公众号纯签约(正式) +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/papay/chapter3_1.shtml +func (w *Client) EntrustPublic(bm gopay.BodyMap) (wxRsp *EntrustPublicResponse, err error) { + err = bm.CheckEmptyError("plan_id", "contract_code", "request_serial", "contract_display_account", "notify_url", "version", "timestamp") + if err != nil { + return nil, err + } + bs, err := w.doProdGet(bm, entrustPublic, SignType_MD5) + if err != nil { + return nil, err + } + wxRsp = new(EntrustPublicResponse) + if err = xml.Unmarshal(bs, wxRsp); err != nil { + return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) + } + return wxRsp, nil +} + +// APP纯签约-预签约接口-获取预签约ID(正式) +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/papay/chapter3_2.shtml +func (w *Client) EntrustAppPre(bm gopay.BodyMap) (wxRsp *EntrustAppPreResponse, err error) { + err = bm.CheckEmptyError("plan_id", "contract_code", "request_serial", "contract_display_account", "notify_url", "version", "timestamp") + if err != nil { + return nil, err + } + bs, err := w.doProdPost(bm, entrustApp, nil) + if err != nil { + return nil, err + } + wxRsp = new(EntrustAppPreResponse) + if err = xml.Unmarshal(bs, wxRsp); err != nil { + return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) + } + return wxRsp, nil +} + +// H5纯签约(正式) +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/papay/chapter3_4.shtml +func (w *Client) EntrustH5(bm gopay.BodyMap) (wxRsp *EntrustH5Response, err error) { + err = bm.CheckEmptyError("plan_id", "contract_code", "request_serial", "contract_display_account", "notify_url", "version", "timestamp", "clientip") + if err != nil { + return nil, err + } + bs, err := w.doProdGet(bm, entrustH5, SignType_HMAC_SHA256) + if err != nil { + return nil, err + } + wxRsp = new(EntrustH5Response) + if err = xml.Unmarshal(bs, wxRsp); err != nil { + return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) + } + return wxRsp, nil +} + +// 支付中签约(正式) +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/papay/chapter3_5.shtml +func (w *Client) EntrustPaying(bm gopay.BodyMap) (wxRsp *EntrustPayingResponse, err error) { + err = bm.CheckEmptyError("contract_mchid", "contract_appid", + "out_trade_no", "nonce_str", "body", "notify_url", "total_fee", + "spbill_create_ip", "trade_type", "plan_id", "contract_code", + "request_serial", "contract_display_account", "contract_notify_url") + if err != nil { + return nil, err + } + bs, err := w.doProdPost(bm, entrustPaying, nil) + if err != nil { + return nil, err + } + wxRsp = new(EntrustPayingResponse) + if err = xml.Unmarshal(bs, wxRsp); err != nil { + return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err) + } + return wxRsp, nil +} diff --git a/wechat/entrust_test.go b/wechat/entrust_test.go new file mode 100644 index 00000000..b4938338 --- /dev/null +++ b/wechat/entrust_test.go @@ -0,0 +1,102 @@ +package wechat + +import ( + "testing" + "time" + + "github.com/iGoogle-ink/gopay" + "github.com/iGoogle-ink/gotil" + "github.com/iGoogle-ink/gotil/xlog" +) + +func TestClient_EntrustPublic(t *testing.T) { + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("plan_id", "12535"). + Set("contract_code", "100000"). + Set("request_serial", "1000"). + Set("contract_display_account", "微信代扣"). + Set("notify_url", "https://www.igoogle.ink"). + Set("version", "1.0"). + Set("timestamp", time.Now().Unix()) + + // 公众号纯签约 + wxRsp, err := client.EntrustPublic(bm) + if err != nil { + xlog.Errorf("client.EntrustPublic(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", wxRsp) +} + +func TestClient_EntrustAppPre(t *testing.T) { + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("plan_id", "12535"). + Set("contract_code", "100000"). + Set("request_serial", "1000"). + Set("contract_display_account", "微信代扣"). + Set("notify_url", "https://www.igoogle.ink"). + Set("version", "1.0"). + Set("timestamp", time.Now().Unix()) + + // APP纯签约 + wxRsp, err := client.EntrustAppPre(bm) + if err != nil { + xlog.Errorf("client.EntrustAppPre(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", wxRsp) +} + +func TestClient_EntrustH5(t *testing.T) { + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("plan_id", "12535"). + Set("contract_code", "100000"). + Set("request_serial", "1000"). + Set("contract_display_account", "微信代扣"). + Set("notify_url", "https://www.igoogle.ink"). + Set("version", "1.0"). + Set("timestamp", time.Now().Unix()). + Set("clientip", "127.0.0.1") + + // H5纯签约 + wxRsp, err := client.EntrustH5(bm) + if err != nil { + xlog.Errorf("client.EntrustH5(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", wxRsp) +} + +func TestClient_EntrustPaying(t *testing.T) { + number := gotil.GetRandomString(32) + xlog.Info("out_trade_no:", number) + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("contract_mchid", mchId). + Set("contract_appid", appId). + Set("out_trade_no", number). + Set("nonce_str", gotil.GetRandomString(32)). + Set("body", "测试签约"). + Set("total_fee", 1). + Set("spbill_create_ip", "127.0.0.1"). + Set("trade_type", TradeType_App). + Set("plan_id", "12535"). + Set("contract_code", "100000"). + Set("request_serial", "1000"). + Set("contract_display_account", "微信代扣"). + Set("notify_url", "https://www.igoogle.ink"). + Set("contract_notify_url", "https://www.igoogle.ink") + + //bm.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8") + + // 支付中签约 + wxRsp, err := client.EntrustPaying(bm) + if err != nil { + xlog.Errorf("client.EntrustPaying(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", wxRsp) +} diff --git a/wechat/merchant.go b/wechat/merchant.go index d75db960..a9bb9491 100644 --- a/wechat/merchant.go +++ b/wechat/merchant.go @@ -2,7 +2,6 @@ package wechat import ( "crypto/tls" - "encoding/json" "encoding/xml" "errors" "fmt" @@ -41,11 +40,11 @@ func (w *Client) Transfer(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12Fil url = w.BaseURL + transfers w.mu.RUnlock() } + req := GenerateXml(bm) if w.DebugSwitch == gopay.DebugOn { - req, _ := json.Marshal(bm) xlog.Debugf("Wechat_Request: %s", req) } - res, bs, errs := httpClient.Post(url).SendString(generateXml(bm)).EndBytes() + res, bs, errs := httpClient.Post(url).SendString(req).EndBytes() if len(errs) > 0 { return nil, errs[0] } @@ -90,11 +89,11 @@ func (w *Client) GetTransferInfo(bm gopay.BodyMap, certFilePath, keyFilePath, pk url = w.BaseURL + getTransferInfo w.mu.RUnlock() } + req := GenerateXml(bm) if w.DebugSwitch == gopay.DebugOn { - req, _ := json.Marshal(bm) xlog.Debugf("Wechat_Request: %s", req) } - res, bs, errs := httpClient.Post(url).SendString(generateXml(bm)).EndBytes() + res, bs, errs := httpClient.Post(url).SendString(req).EndBytes() if len(errs) > 0 { return nil, errs[0] } @@ -141,11 +140,11 @@ func (w *Client) PayBank(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12File url = w.BaseURL + payBank w.mu.RUnlock() } + req := GenerateXml(bm) if w.DebugSwitch == gopay.DebugOn { - req, _ := json.Marshal(bm) xlog.Debugf("Wechat_Request: %s", req) } - res, bs, errs := httpClient.Post(url).SendString(generateXml(bm)).EndBytes() + res, bs, errs := httpClient.Post(url).SendString(req).EndBytes() if len(errs) > 0 { return nil, errs[0] } @@ -189,11 +188,11 @@ func (w *Client) QueryBank(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12Fi url = w.BaseURL + queryBank w.mu.RUnlock() } + req := GenerateXml(bm) if w.DebugSwitch == gopay.DebugOn { - req, _ := json.Marshal(bm) xlog.Debugf("Wechat_Request: %s", req) } - res, bs, errs := httpClient.Post(url).SendString(generateXml(bm)).EndBytes() + res, bs, errs := httpClient.Post(url).SendString(req).EndBytes() if len(errs) > 0 { return nil, errs[0] } @@ -232,11 +231,11 @@ func (w *Client) GetRSAPublicKey(bm gopay.BodyMap, certFilePath, keyFilePath, pk bm.Set("sign", getReleaseSign(w.ApiKey, bm.Get("sign_type"), bm)) httpClient := xhttp.NewClient().SetTLSConfig(tlsConfig).Type(xhttp.TypeXML) + req := GenerateXml(bm) if w.DebugSwitch == gopay.DebugOn { - req, _ := json.Marshal(bm) xlog.Debugf("Wechat_Request: %s", req) } - res, bs, errs := httpClient.Post(url).SendString(generateXml(bm)).EndBytes() + res, bs, errs := httpClient.Post(url).SendString(req).EndBytes() if len(errs) > 0 { return nil, errs[0] } @@ -283,20 +282,6 @@ func (w *Client) profitSharing(bm gopay.BodyMap, uri string, certFilePath, keyFi return nil, err } - arr, err := bm.GetArrayBodyMap("receivers") - if err != nil { - return nil, err - } - if len(arr) == 0 { - return nil, errors.New("receivers is empty") - } - // 检查每个分账接收者的必传属性 - for _, r := range arr { - err = r.CheckEmptyError("type", "account", "amount", "description") - if err != nil { - return nil, err - } - } // 设置签名类型,官方文档此接口只支持 HMAC_SHA256 bm.Set("sign_type", SignType_HMAC_SHA256) tlsConfig, err := w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath) @@ -354,15 +339,6 @@ func (w *Client) ProfitSharingAddReceiver(bm gopay.BodyMap) (wxRsp *ProfitSharin if err != nil { return nil, err } - // 输入参数 接收方 - r, err := bm.GetBodyMap("receiver") - if err != nil { - return nil, err - } - err = r.CheckEmptyError("type", "account", "relation_type") - if err != nil { - return nil, err - } // 设置签名类型,官方文档此接口只支持 HMAC_SHA256 bm.Set("sign_type", SignType_HMAC_SHA256) bs, err := w.doProdPost(bm, profitSharingAddReceiver, nil) @@ -385,15 +361,6 @@ func (w *Client) ProfitSharingRemoveReceiver(bm gopay.BodyMap) (wxRsp *ProfitSha if err != nil { return nil, err } - // 输入参数 接收方 - r, err := bm.GetBodyMap("receiver") - if err != nil { - return nil, err - } - err = r.CheckEmptyError("type", "account") - if err != nil { - return nil, err - } // 设置签名类型,官方文档此接口只支持 HMAC_SHA256 bm.Set("sign_type", SignType_HMAC_SHA256) bs, err := w.doProdPost(bm, profitSharingRemoveReceiver, nil) diff --git a/wechat/merchant_test.go b/wechat/merchant_test.go new file mode 100644 index 00000000..196a3941 --- /dev/null +++ b/wechat/merchant_test.go @@ -0,0 +1,147 @@ +package wechat + +import ( + "encoding/json" + "testing" + + "github.com/iGoogle-ink/gopay" + "github.com/iGoogle-ink/gotil" + "github.com/iGoogle-ink/gotil/xlog" + "github.com/iGoogle-ink/gotil/xrsa" +) + +func TestClient_Transfer(t *testing.T) { + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("nonce_str", gotil.GetRandomString(32)). + Set("partner_trade_no", gotil.GetRandomString(32)). + Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8"). + Set("check_name", "FORCE_CHECK"). // NO_CHECK:不校验真实姓名 , FORCE_CHECK:强校验真实姓名 + Set("re_user_name", "付明明"). // 收款用户真实姓名。 如果check_name设置为FORCE_CHECK,则必填用户真实姓名 + Set("amount", 30). // 企业付款金额,单位为分 + Set("desc", "测试转账"). // 企业付款备注,必填。注意:备注中的敏感词会被转成字符* + Set("spbill_create_ip", "127.0.0.1") + + // 企业向微信用户个人付款(不支持沙箱环境) + // body:参数Body + // certFilePath:cert证书路径 + // keyFilePath:Key证书路径 + // pkcs12FilePath:p12证书路径 + wxRsp, err := client.Transfer(bm, nil, nil, nil) + if err != nil { + xlog.Errorf("client.Transfer(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", *wxRsp) +} + +func Test_ProfitSharing(t *testing.T) { + type Receiver struct { + Type string `json:"type"` + Account string `json:"account"` + Amount int `json:"amount"` + Description string `json:"description"` + } + + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("nonce_str", gotil.GetRandomString(32)). + Set("transaction_id", "4208450740201411110007820472"). + Set("out_order_no", "P20150806125346") + + var rs []*Receiver + item := &Receiver{ + Type: "MERCHANT_ID", + Account: "190001001", + Amount: 100, + Description: "分到商户", + } + rs = append(rs, item) + item2 := &Receiver{ + Type: "PERSONAL_OPENID", + Account: "86693952", + Amount: 888, + Description: "分到个人", + } + rs = append(rs, item2) + bs, _ := json.Marshal(rs) + + bm.Set("receivers", string(bs)) + + wxRsp, err := client.ProfitSharing(bm, nil, nil, nil) + if err != nil { + xlog.Errorf("client.ProfitSharingAddReceiver(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", wxRsp) +} + +func Test_ProfitSharingAddReceiver(t *testing.T) { + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("nonce_str", gotil.GetRandomString(32)) + + receiver := make(gopay.BodyMap) + receiver.Set("type", "MERCHANT_ID"). + Set("account", "190001001"). + Set("name", "商户全称"). + Set("relation_type", "STORE_OWNER") + + bm.Set("receiver", receiver.JsonBody()) + + wxRsp, err := client.ProfitSharingAddReceiver(bm) + if err != nil { + xlog.Errorf("client.ProfitSharingAddReceiver(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", wxRsp) +} + +func Test_ProfitSharingRemoveReceiver(t *testing.T) { + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("nonce_str", gotil.GetRandomString(32)) + + receiver := make(gopay.BodyMap) + receiver.Set("type", "MERCHANT_ID"). + Set("account", "190001001") + + bm.Set("receiver", receiver.JsonBody()) + + wxRsp, err := client.ProfitSharingRemoveReceiver(bm) + if err != nil { + xlog.Errorf("client.ProfitSharingRemoveReceiver(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", wxRsp) +} + +func TestClient_PayBank(t *testing.T) { + // 初始化参数结构体 + bm := make(gopay.BodyMap) + bm.Set("partner_trade_no", mchId). + Set("nonce_str", gotil.GetRandomString(32)). + Set("bank_code", "1001"). // 招商银行,https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_4&index=5 + Set("amount", 1) + + encryptBank, err := xrsa.RsaEncryptDataV2(xrsa.PKCS1, []byte("621400000000567"), "publicKey.pem") + if err != nil { + xlog.Error(err) + return + } + encryptName, err := xrsa.RsaEncryptDataV2(xrsa.PKCS1, []byte("Jerry"), "publicKey.pem") + if err != nil { + xlog.Error(err) + return + } + bm.Set("enc_bank_no", encryptBank). + Set("enc_true_name", encryptName) + + // 企业付款到银行卡API + wxRsp, err := client.PayBank(bm, "certFilePath", "keyFilePath", "pkcs12FilePath") + if err != nil { + xlog.Errorf("client.EntrustPaying(%+v),error:%+v", bm, err) + return + } + xlog.Debug("wxRsp:", wxRsp) +} diff --git a/wechat/param.go b/wechat/param.go index 8cba2517..05c5c3a4 100644 --- a/wechat/param.go +++ b/wechat/param.go @@ -231,7 +231,7 @@ func getSanBoxSignKey(mchId, nonceStr, sign string) (key string, err error) { reqs.Set("sign", sign) keyResponse := new(getSignKeyResponse) - _, errs := xhttp.NewClient().Type(xhttp.TypeXML).Post(sandboxGetSignKey).SendString(generateXml(reqs)).EndStruct(keyResponse) + _, errs := xhttp.NewClient().Type(xhttp.TypeXML).Post(sandboxGetSignKey).SendString(GenerateXml(reqs)).EndStruct(keyResponse) if len(errs) > 0 { return gotil.NULL, errs[0] } @@ -242,7 +242,7 @@ func getSanBoxSignKey(mchId, nonceStr, sign string) (key string, err error) { } // 生成请求XML的Body体 -func generateXml(bm gopay.BodyMap) (reqXml string) { +func GenerateXml(bm gopay.BodyMap) (reqXml string) { bs, err := xml.Marshal(bm) if err != nil { return gotil.NULL diff --git a/wechat/payment_api.go b/wechat/payment_api.go index 2c88e3b6..012c06a4 100644 --- a/wechat/payment_api.go +++ b/wechat/payment_api.go @@ -393,7 +393,7 @@ func GetOpenIdByAuthCode(appId, mchId, apiKey, authCode, nonceStr string) (openI bm.Set("sign", getReleaseSign(apiKey, SignType_MD5, bm)) openIdRsp = new(OpenIdByAuthCodeRsp) - _, errs := xhttp.NewClient().Type(xhttp.TypeXML).Post(url).SendString(generateXml(bm)).EndStruct(openIdRsp) + _, errs := xhttp.NewClient().Type(xhttp.TypeXML).Post(url).SendString(GenerateXml(bm)).EndStruct(openIdRsp) if len(errs) > 0 { return nil, errs[0] } diff --git a/wechat/payment_api_test.go b/wechat/payment_api_test.go new file mode 100644 index 00000000..bdc7c6ec --- /dev/null +++ b/wechat/payment_api_test.go @@ -0,0 +1,35 @@ +package wechat + +import ( + "testing" + + "github.com/iGoogle-ink/gotil/xlog" +) + +func TestDecryptOpenDataToBodyMap(t *testing.T) { + data := "Kf3TdPbzEmhWMuPKtlKxIWDkijhn402w1bxoHL4kLdcKr6jT1jNcIhvDJfjXmJcgDWLjmBiIGJ5acUuSvxLws3WgAkERmtTuiCG10CKLsJiR+AXVk7B2TUQzsq88YVilDz/YAN3647REE7glGmeBPfvUmdbfDzhL9BzvEiuRhABuCYyTMz4iaM8hFjbLB1caaeoOlykYAFMWC5pZi9P8uw==" + iv := "Cds8j3VYoGvnTp1BrjXdJg==" + session := "lyY4HPQbaOYzZdG+JcYK9w==" + + //解密开放数据 + // encryptedData:包括敏感数据在内的完整用户信息的加密数据 + // iv:加密算法的初始向量 + // sessionKey:会话密钥 + bm, err := DecryptOpenDataToBodyMap(data, iv, session) + if err != nil { + xlog.Error(err) + return + } + xlog.Debug("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 { + xlog.Error(err) + return + } + xlog.Debug("refundNotify:", *refundNotify) +}