微信小微商户直连接口详解

项目背景:

       小微商户根据接口申请入驻、查看状态、进行签约。商户信息配置、Native扫码及小程序发起支付、查询订单、进行退款、查询退款。小微商户资料修改及提现。

 一、小微商户进件

遇到问题:

       1、返回错误信息及解决办法

                 (1)、输入商户号后、平台提示证书不存在。API文档中也没有具体解释(如图1-1)?

图 1-1

               已解决:关键在于参数中的商户号输入错误,经过沟通发现该商户号有两个,换个账号就可以解决问题

              (2)解密敏感信息失败                                        已解决--------仔细查看需要进行加密的参数,当时手机号没有加密导致微信方解密敏感信息失败

              (3)签名失败                                                      已解决--------参数中的sign字段应方在最后再进行签名,不能跟api中的参数顺序一样

              (4)身份证正面识别失败                                    已解决--------过期或失效的身份证无法识别成功

        2、Java实现AES加密,抛出异常(如图1-2):

图 1-2

                去该地址http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html下载${jdk_home}/jre/lib/security和${jre_home}/lib/security目录下的 local_policy.jar 和 US_export_policy.jar文件并且将其覆盖   

        3、小微商户进件流程

  • 申  请  入  驻:  (1)、先获取平台证书及序列号,并用返回的参数对证书进行解密得到平台证书的原文。ps:该原文用于对敏感信息加密

                                    (2)、使用加密方法对敏感信息进行加密

                                    (3)、将图片上传到接口获取图片id填写到请求入驻参数中

                                    (4)、剩下的参数依次填入map中最后进行签名使用证书通过接口上传商户信息,ps:注意中文编码

  • 查询申请状态:(1)、查询时使用自定义的编号或者微信返回的申请单号其一即可查询,ps:签名也是在最后的参数

                                    (2)、将会返回审核状态,如果已审核成功变成待签约状态将会返回链接,商户扫码即可

 二、小微商户配置

       遇到问题:

        1、 返回错误信息及解决办法

                (1)绑定APPID时注意接口sub_appid参数示例                           已解决:此处的sub_appid对小程序支付有影响,需配置为小程序的id否则在下单时会造成公众号id与sub_appid不匹配

         2、配置流程

                (1)将商户的商户号与固定小程序id绑定即可

三、小微商户交易

        遇到问题:(appid-公众号id,mchid-商户号,subappid-小程序id及签名所需公钥请联系相关人员)

          1、返回错误信息及解决办法

                  (1)参数的必填项为可选填,结果返回缺少参数                         已解决:每个参数看好备注,不同的交易方式参数的可填项不一样

                  (2)native交易方式返回的支付URL打开无法支付                      已解决:必须生成二维码后进行扫码支付,无法直接打开链接支付

                  (3)每天在没有余额时退款失败                                                  已解决:每天都会自动提现到银行卡内导致账户无余额,无法直接退款,必须有余额

                  (4)查询退款时可能有多单返回                                                  已解决:查询退款时,尽量使用退款单号查询,一个订单号会有退款总金额不超过订单金额的退款单数,当超过20单时必须使用退款单号    

          2、交易流程

                 (1)提交自己内部的唯一商品订单号、商品详情、金额(分)、交易方式等参数返回扫码地址或小程序调起支付的参数

                 (2)查询订单时提供商户号、微信订单号或商户订单号即可

                 (3)订单生成后不能马上调用关单接口,最短调用时间间隔为5分钟,提供商户号、商户订单号即可关闭订单

                 (4)必须提供微信及内部订单号、内部唯一退款单号、订单总金额、退款金额申请退款,一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号再次申请退款

                 (5)查询退款提供退款单号即可

 四、小微商户资料修改及提现

           修改及提现流程:

             (1)输入商户号及提现单的日期即可,提现单的日期是前一天的金额的提现,不是当天的交易金额

             (2)提供商户号及银行名称、加密后的银行卡号即可修改成功

             (3)重新发起提现时,如果提现一号的交易请提供2号的日期

             (4)联系人信息提供后即可修改信息

 五、代码实现

    本项目使用SSM模型开发

  1 public class WeiXinAccountController {
  2     @Resource
  3     WeiXinAccountService weiXinAccountService;
  4     /**
  5      * 上传图片的序列id
  6      * @return 状态
  7      */
  8     public ReturnModel<?> getPhotoid(@RequestBody WXPhotoModel wxPhotoModel){
  9         ReturnModel returnModel = weiXinAccountService.getPhotoid(wxPhotoModel);
 10         return returnModel;
 11     }
 12 
 13     /**
 14      * 查询审核状态
 15      * @return 状态
 16      */
 17     public ReturnModel<?> queryForApply(@RequestBody WXApplyStateModel wxApplyStateModel){
 18         ReturnModel returnModel = weiXinAccountService.queryForApply(wxApplyStateModel);
 19         return returnModel;
 20     }
 21 
 22     /**
 23      * 提交参数进行审核
 24      * @param wxApplyForEntryModel 参数
 25      * @return 是否提交成功//商户存在时不可编辑(不可再次提交)
 26      */
 27     public ReturnModel<?> submitInfo(@RequestBody WXApplyForEntryModel wxApplyForEntryModel){
 28         return weiXinAccountService.submitInfo(wxApplyForEntryModel);
 29     }
 30 
 31     /***
 32      * 绑定AppID配置
 33      * @param wxAddSubDevConfigModel
 34      * @throws
 35      */
 36     public ReturnModel<?> addSubDevConfig(@RequestBody WXAddSubDevConfigModel wxAddSubDevConfigModel){
 37         return weiXinAccountService.addSubDevConfig(wxAddSubDevConfigModel);
 38     }
 39 
 40     /**
 41      * 统一下单native
 42      * @param wxUnifiedOrderModel
 43      * @return
 44      */
 45     public ReturnModel<?> unifiedOrderByNATIVE(@RequestBody WXUnifiedOrderModel wxUnifiedOrderModel, HttpServletRequest httpServletRequest){
 46         String ip = WXHttpUtil.getSpbillCreateIp(httpServletRequest);
 47         wxUnifiedOrderModel.setSpbillCreateIp(ip);
 48         return weiXinAccountService.unifiedOrderByNATIVE(wxUnifiedOrderModel);
 49     }
 50     /**
 51      * 统一下单jsapi
 52      * @param wxUnifiedOrderModel
 53      * @return
 54      */
 55     public ReturnModel<?> unifiedOrderByJSAPI(@RequestBody WXUnifiedOrderModel wxUnifiedOrderModel, HttpServletRequest httpServletRequest){
 56         String ip = WXHttpUtil.getSpbillCreateIp(httpServletRequest);
 57         wxUnifiedOrderModel.setSpbillCreateIp(ip);
 58         return weiXinAccountService.unifiedOrderByJSAPI(wxUnifiedOrderModel);
 59     }
 60     /**
 61      * 查询订单
 62      * @param wxOrderQueryModel
 63      * @return
 64      */
 65     public ReturnModel<?> orderQuery(@RequestBody WXOrderQueryModel wxOrderQueryModel){
 66         return weiXinAccountService.orderQuery(wxOrderQueryModel);
 67     }
 68     /**
 69      * 关闭订单,如果支付失败则需要关闭订单避免重复支付或超时系统退出不再受理也需要关闭
 70      * @param wxCloseOrderModel
 71      * @return
 72      */
 73     public ReturnModel<?> closeOrder(@RequestBody WXCloseOrderModel wxCloseOrderModel){
 74         return weiXinAccountService.closeOrder(wxCloseOrderModel);
 75     }
 76 
 77     /**
 78      * 申请退款
 79      * @param wxRefundModel
 80      * @return
 81      */
 82     public ReturnModel<?> refund(@RequestBody WXRefundModel wxRefundModel){
 83         return weiXinAccountService.refund(wxRefundModel);
 84     }
 85 
 86     /**
 87      * 查询退款
 88      * @param wxRefundQueryModel
 89      * @return
 90      */
 91     public ReturnModel<?> refundQuery(@RequestBody WXRefundQueryModel wxRefundQueryModel){
 92         return weiXinAccountService.refundQuery(wxRefundQueryModel);
 93     }
 94 
 95     /**
 96      * 提现状态查询
 97      * @param wxQueryAutoWithdrawByDate
 98      * @return
 99      */
100     public ReturnModel<?> queryAutoWithdrawByDate(@RequestBody WXQueryAutoWithdrawByDate wxQueryAutoWithdrawByDate){
101         return weiXinAccountService.queryAutoWithdrawByDate(wxQueryAutoWithdrawByDate);
102     }
103 
104     /**
105      * 修改银行卡号
106      * @param modifyarchives
107      * @return
108      * @throws Exception
109      */
110     public ReturnModel<?> modifyArchives(@RequestBody WXModifyArchivesModel modifyarchives) throws Exception {
111         return weiXinAccountService.modifyArchives(modifyarchives);
112     }
113 
114     /**
115      * 重新发起提现
116      * @param reautowithdrawbydate
117      * @return
118      */
119     public ReturnModel<?> reAutoWithdrawByDate(@RequestBody WXQueryAutoWithdrawByDate reautowithdrawbydate) {
120         return weiXinAccountService.reAutoWithdrawByDate(reautowithdrawbydate);
121     }
122 
123     /**
124      * 修改商户联系信息
125      * @param modifycontactinfo
126      * @return
127      *
128      * @throws Exception
129      */
130     public ReturnModel<?> modifyContactInfo(@RequestBody WXModifyContactInfoModel modifycontactinfo) throws Exception {
131         return weiXinAccountService.modifyContactInfo(modifycontactinfo);
132     }
133 }
WXController
  1 public class WeiXinAccountServiceImpl implements WeiXinAccountService {
  2     private final Logger LOGGER = LoggerFactory.getLogger(WeiXinAccountServiceImpl.class);
  3     /**
  4      * 准备申请入驻的参数
  5      * @return 入驻的参数
  6      * @throws Exception
  7      */
  8     public SortedMap<String, String> applyForEntry(WXApplyForEntryModel wxApplyForEntryModel) throws Exception {
  9         //先获取平台证书及序列号,并用返回的参数对证书进行解密得到平台证书的原文。
 10         // ps:该原文用于对敏感信息加密
 11         String certificatesInfo = getCertficates();
 12         if(("fail").equals(certificatesInfo) || ("").equals(certificatesInfo)){
 13             return null;
 14         }
 15         JSONArray data = JSONObject.parseArray(
 16                 JSONObject.parseObject(certificatesInfo).getString("data"));
 17         //得到平台系列号
 18         String serialNo = JSONObject.parseObject(
 19                 JSONObject.toJSONString(data.get(0))).getString("serial_no");
 20         //得到用于加密敏感信息的证书原文
 21         String cert = WXPayUtil.getWXCert(data);
 22         String name = RSAEncryptUtil.rsaEncrypt(wxApplyForEntryModel.getIdCardName(),cert);
 23         wxApplyForEntryModel.setVersion("3.0");
 24         wxApplyForEntryModel.setCertSn(serialNo);
 25         wxApplyForEntryModel.setMchId(WXConstant.MCHID);
 26         wxApplyForEntryModel.setNonceStr(String.valueOf(new Date().getTime()));
 27         wxApplyForEntryModel.setSignType(WXConstant.SIGNTTYPE);
 28         wxApplyForEntryModel.setIdCardNumber(
 29                 RSAEncryptUtil.rsaEncrypt(wxApplyForEntryModel.getIdCardNumber(),cert));
 30         wxApplyForEntryModel.setIdCardName(name);
 31         wxApplyForEntryModel.setAccountName(name);
 32         wxApplyForEntryModel.setAccountNumber(
 33                 RSAEncryptUtil.rsaEncrypt(wxApplyForEntryModel.getAccountNumber(),cert));
 34         wxApplyForEntryModel.setContact(name);
 35         wxApplyForEntryModel.setContactPhone(
 36                 RSAEncryptUtil.rsaEncrypt(wxApplyForEntryModel.getContactPhone(),cert));
 37         SortedMap reqPara = AnnotationUtil.parseObjectToMap(wxApplyForEntryModel);
 38         return reqPara;
 39     }
 40     /**
 41      * 得到平台证书包含的参数json
 42      * @return
 43      */
 44     public String getCertficates(){
 45         SortedMap<String, String> reqDataToZS = new TreeMap<>();
 46         reqDataToZS.put("mch_id", WXConstant.MCHID);//服务商商户号
 47         reqDataToZS.put("nonce_str", WXPayUtil.getRandomString(32));//随机字符串
 48         reqDataToZS.put("sign_type", WXConstant.SIGNTTYPE);//签名类型
 49         reqDataToZS.put("sign", WXPayUtil.generateSignature(reqDataToZS,WXConstant.KEY,WXConstant.HMACSHA256TYPE));//签名
 50         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithoutCert(WXConstant.ZHENGSHUURL,reqDataToZS);//证书接口返回所需参数
 51         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
 52         if(("FAIL").equals(result.get("result_code"))){
 53             return "fail";
 54         }
 55         return result.get("certificates");
 56     }
 57 
 58     /**
 59      * 得到上传照片的id
 60      * @param wxPhotoModel 图片
 61      * @return 图片id
 62      */
 63     @Override
 64     public ReturnModel<?> getPhotoid(WXPhotoModel wxPhotoModel){
 65         String cent = "";
 66         InputStream fileInputStream = null;
 67         DataInputStream dis = null;
 68         byte[] bufferOut;
 69         try {
 70             URL url = new URL(wxPhotoModel.getFileUrlPath());
 71             HttpURLConnection httpUrl = (HttpURLConnection) url.openConnection();
 72             httpUrl.setDoInput(true);
 73             httpUrl.setRequestMethod("GET");
 74             fileInputStream = httpUrl.getInputStream();
 75             dis = new DataInputStream(fileInputStream);
 76             bufferOut = new byte[httpUrl.getContentLength()];
 77             dis.read(bufferOut);
 78             cent = MD5.md5HashCode(bufferOut);
 79         } catch (Exception e) {
 80             LOGGER.error(e.getMessage());
 81             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),e.getMessage());
 82         }finally {
 83             if (fileInputStream!=null) {
 84                 try {
 85                     fileInputStream.close();
 86                 } catch (IOException e) {
 87                     LOGGER.error(e.getMessage());
 88                     return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),e.getMessage());
 89                 }
 90             }
 91             if(dis!=null){
 92                 try {
 93                     dis.close();
 94                 } catch (IOException e) {
 95                     LOGGER.error(e.getMessage());
 96                     return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),e.getMessage());
 97                 }
 98             }
 99         }
100         if ("".equals(cent) || fileInputStream == null)
101             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"图片未获取到");
102         SortedMap<String, String> reqData = new TreeMap<>();
103         reqData.put("mch_id", WXConstant.MCHID);
104         reqData.put("sign_type", WXConstant.SIGNTTYPE);
105         reqData.put("media_hash", cent);
106         reqData.put("sign", WXPayUtil.generateSignature(reqData,
107                 WXConstant.KEY, WXConstant.HMACSHA256TYPE));
108         MultipartEntityBuilder builder = MultipartEntityBuilder.create();
109         builder.addTextBody("mch_id",WXConstant.MCHID, ContentType.MULTIPART_FORM_DATA);
110         builder.addBinaryBody("media",bufferOut, ContentType.DEFAULT_BINARY,wxPhotoModel.getFileName());
111         builder.addTextBody("media_hash",cent, ContentType.MULTIPART_FORM_DATA);
112         builder.addTextBody("sign_type",WXConstant.SIGNTTYPE, ContentType.MULTIPART_FORM_DATA);
113         builder.addTextBody("sign",reqData.get("sign"), ContentType.MULTIPART_FORM_DATA);
114         String returnStr = WXPayHttpUtil.WXHttpPostFormWithCert(WXConstant.MEDIAUURL,
115                 WXConstant.MCHID,WXConstant.CERTPATH,builder);
116         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
117         if(result.isEmpty()){
118             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"未获取到微信方图片id");
119         }
120         return new ReturnModel<>(result);
121     }
122 
123     @Override
124     public ReturnModel<?> unifiedOrderByNATIVE(WXUnifiedOrderModel wxUnifiedOrderModel) {
125         wxUnifiedOrderModel.setProductId(WXPayUtil.getRandomString(32));
126         wxUnifiedOrderModel.setTradeType("NATIVE");
127         return unifiedOrder(wxUnifiedOrderModel);
128     }
129 
130     @Override
131     public ReturnModel<?> unifiedOrderByJSAPI(WXUnifiedOrderModel wxUnifiedOrderModel) {
132         wxUnifiedOrderModel.setSubAppid(WXConstant.SUBAPPID);
133         wxUnifiedOrderModel.setTradeType("JSAPI");
134         return unifiedOrder(wxUnifiedOrderModel);
135     }
136 
137     /**
138      * 查询审核状态
139      * @param applyState 参数-商户号、服务商自定义的用户编号
140      * @return
141      */
142     @Override
143     public ReturnModel<?> queryForApply(WXApplyStateModel applyState){
144         applyState.setVersion("1.0");
145         applyState.setMchId(WXConstant.MCHID);
146         applyState.setNonceStr(WXPayUtil.getRandomString(32));
147         applyState.setSignType(WXConstant.SIGNTTYPE);
148         SortedMap<String,String> reqData = AnnotationUtil.parseObjectToMap(applyState);
149         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithCert(WXConstant.QUERYURL,
150                 reqData,WXConstant.CERTPATH);
151         if(("").equals(returnStr)){
152             return new ReturnModel<>(ErrorCode.SERVER_ERROR.getCode(),"安全证书无法解析");
153         }
154         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
155         if(("FAIL").equals(result.get("return_code"))){
156             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),result.get("return_msg"));
157         }
158         if(("SUCCESS").equals(result.get("return_code"))){
159             return new ReturnModel<>(result);
160 //            String applymentStateDesc = "申请状态:"+result.get("applyment_state_desc");
161 //            if(("REJECTED").equals(result.get("applyment_state"))){
162 //                //审核结果json
163 //                JSONArray audit_detail = JSONObject.parseArray(
164 //                        JSONObject.parseObject(
165 //                                result.get("audit_detail")).getString("audit_detail"));
166 //                //得到审核详情
167 //                String reject_reason = JSONObject.parseObject(
168 //                        JSONObject.toJSONString(audit_detail.get(0))).getString("reject_reason");
169 //                return applymentStateDesc+"\t审核详情:"+reject_reason;
170 //            }
171 //            if(("TO_BE_SIGNED").equals(result.get("applyment_state"))){
172 //                return applymentStateDesc+"\t小微商户号:"
173 //                        +result.get("sub_mch_id")+"\t签约链接:"
174 //                        +result.get("sign_url");
175 //            }
176 //            if(("FINISH").equals(result.get("applyment_state"))){
177 //                return applymentStateDesc+"\t小微商户号:"+result.get("sub_mch_id");
178 //            }
179 //            return applymentStateDesc;
180         }
181         return new ReturnModel<>(ErrorCode.SERVER_ERROR.getCode(),"结果解析错误");
182     }
183 
184     /**
185      * 提交商户申请
186      * @param wxApplyForEntryModel 请求商户信息
187      * @return
188      */
189     @Override
190     public ReturnModel<?> submitInfo(WXApplyForEntryModel wxApplyForEntryModel) {
191         SortedMap<String,String> reqData = new TreeMap<>();
192         try {
193             reqData = applyForEntry(wxApplyForEntryModel);
194         } catch (Exception e) {
195             LOGGER.error(e.getMessage());
196 
197         }
198         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithCert(WXConstant.SUBMITURL,
199                 reqData, WXConstant.CERTPATH);
200         if(("").equals(returnStr)){
201             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"安全证书无法解析");
202         }
203         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
204         if(("FAIL").equals(result.get("return_code"))){
205             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),
206                     "返回信息:"+result.get("return_msg")+"错误描述:"+result.get("err_code_des"));
207         }
208         if(("SUCCESS").equals(result.get("return_code"))){
209             return new ReturnModel<>(result);
210 //            if(("SUCCESS").equals(result.get("result_code"))){
211 //                return "微信分配的申请单号:"+result.get("applyment_id");
212 //            }
213 //            if(("FAIL").equals(result.get("result_code"))){
214 //                if(("EXIST").equals(result.get("err_code"))){
215 //                    return "商户已存在,对应的申请单当前状态不可编辑";
216 //                }
217 //                return result.get("err_code_des");
218 //            }
219         }
220         return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"结果解析错误");
221     }
222 
223     /**
224      * 提现状态
225      * @param wxQueryAutoWithdrawByDate
226      * @return
227      */
228     @Override
229     public ReturnModel<?> queryAutoWithdrawByDate(WXQueryAutoWithdrawByDate wxQueryAutoWithdrawByDate) {
230         wxQueryAutoWithdrawByDate.setMchId(WXConstant.MCHID);
231         wxQueryAutoWithdrawByDate.setSignType(WXConstant.SIGNTTYPE);
232         wxQueryAutoWithdrawByDate.setNonceStr(WXPayUtil.getRandomString(32));
233         SortedMap<String,String> reqData = AnnotationUtil.parseObjectToMap(wxQueryAutoWithdrawByDate);
234         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithCert(WXConstant.QUERYAUTOWITHDRAWURL,
235                 reqData,WXConstant.CERTPATH);
236         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
237         if("SUCCESS".equals(result.get("return_code"))){
238             return new ReturnModel<>(result);
239 //            if("SUCCESS".equals(result.get("result_code"))){
240 //                if ("PROCESSING".equals(result.get("withdraw_status"))){
241 //                    return result.get("withdraw_status");
242 //                }else if ("SUCCESS".equals(result.get("withdraw_status"))){
243 //                    return result.get("date")+"\t金额(分):"+result.get("amount")+"\t提现成功时间:"
244 //                            +result.get("success_time");
245 //                }
246 //                return result.get("withdraw_status");
247 //            }else if ("FAIL".equals(result.get("result_code"))){
248 //                if(result.get("err_code_des") != null && !"".equals(result.get("err_code_des"))){
249 //                    return result.get("err_code_des");
250 //                }
251 //                return result.get("err_code");
252 //            }
253         }else if("FAIL".equals(result.get("return_code"))){
254             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),result.get("return_msg"));
255         }
256         return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"结果解析错误");
257     }
258 
259     /**
260      * 统一下单
261      * @param wxUnifiedOrderModel
262      * @return
263      */
264     @Override
265     public ReturnModel<?> unifiedOrder(WXUnifiedOrderModel wxUnifiedOrderModel) {
266         wxUnifiedOrderModel.setAppid(WXConstant.APPID);
267         wxUnifiedOrderModel.setMchId(WXConstant.MCHID);
268         wxUnifiedOrderModel.setNonceStr(WXPayUtil.getRandomString(32));
269         wxUnifiedOrderModel.setSignType(WXConstant.SIGNTTYPE);
270         wxUnifiedOrderModel.setNotifyUrl(WXConstant.NOTIFYURL);
271         SortedMap<String,String> reqData = AnnotationUtil.parseObjectToMap(wxUnifiedOrderModel);
272         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithoutCert(WXConstant.UNIFIEDORDERURL, reqData);
273         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
274         if(("SUCCESS").equals(result.get("return_code"))){
275             if(("SUCCESS").equals(result.get("result_code"))){
276 //                if ("NATIVE".equals(wxUnifiedOrderModel.getTradeType())){
277 //                    return new ReturnModel<>(result);
278 //                }else
279                     if("JSAPI".equals(wxUnifiedOrderModel.getTradeType())){
280                     Map<String,String> payinfo = new HashMap<>();
281                     payinfo.put("appId",wxUnifiedOrderModel.getAppid());
282                     payinfo.put("nonceStr",WXPayUtil.getRandomString(32));
283                     payinfo.put("package","prepay_id="+result.get("prepay_id"));
284                     payinfo.put("signType",WXConstant.MD5TYPE);
285                     payinfo.put("timeStamp",String.valueOf(new Date().getTime()));
286                     payinfo.put("paySign",WXPayUtil.generateSignature(payinfo,WXConstant.KEY,WXConstant.MD5TYPE));
287 //                    String info = JSONObject.toJSONString(payinfo);
288                     return new ReturnModel<>(payinfo);
289                 }
290             }
291 //            if(("FAIL").equals(result.get("result_code"))){
292 //                return result.get("err_code_des");
293 //            }
294             return new ReturnModel<>(result);
295         }else if(("FAIL").equals(result.get("return_code"))){
296             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),result.get("return_msg"));
297         }
298         return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"结果解析错误");
299     }
300 
301     /**
302      * 绑定AppID配置
303      * @param wxAddSubDevConfigModel
304      * @return
305      */
306     @Override
307     public ReturnModel<?> addSubDevConfig(WXAddSubDevConfigModel wxAddSubDevConfigModel) {
308         wxAddSubDevConfigModel.setAppid(WXConstant.APPID);
309         wxAddSubDevConfigModel.setMchId(WXConstant.MCHID);
310         wxAddSubDevConfigModel.setSubAppid(WXConstant.SUBAPPID);
311         SortedMap<String,String> reqData = AnnotationUtil.parseObjectToMap(wxAddSubDevConfigModel);
312         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithCert(WXConstant.ADDSUBDEVCONFIGURL, reqData, WXConstant.CERTPATH);
313         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
314         if(("SUCCESS").equals(result.get("return_code"))) {
315             return new ReturnModel<>(result);
316 //            if(("SUCCESS").equals(result.get("result_code"))){
317 //                return "配置成功";
318 //            }else if(("FAIL").equals(result.get("result_code"))){
319 //                return result.get("err_code_des").replaceAll("[a-zA-Z]","");
320 //            }
321         }else if(("FAIL").equals(result.get("return_code"))){
322             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),result.get("return_msg"));
323         }
324         return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"结果解析错误");
325     }
326 
327     /**
328      * 订单查询
329      * @param wxOrderQueryModel
330      * @return
331      */
332     @Override
333     public ReturnModel<?> orderQuery(WXOrderQueryModel wxOrderQueryModel) {
334         wxOrderQueryModel.setSubAppid(WXConstant.SUBAPPID);
335         wxOrderQueryModel.setAppid(WXConstant.APPID);
336         wxOrderQueryModel.setMchId(WXConstant.MCHID);
337         wxOrderQueryModel.setNonceStr(WXPayUtil.getRandomString(32));
338         wxOrderQueryModel.setSignType(WXConstant.SIGNTTYPE);
339         SortedMap<String,String> reqData = AnnotationUtil.parseObjectToMap(wxOrderQueryModel);
340         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithoutCert(WXConstant.ORDERQUERYURL, reqData);
341         Map<String,String> result= WXPayXmlUtil.parseXMLToMap(returnStr);
342         if(("SUCCESS").equals(result.get("return_code"))){
343             return new ReturnModel<>(result);
344 //            if(("SUCCESS").equals(result.get("result_code")) && ("SUCCESS").equals(result.get("trade_state"))){
345 //                return result.get("trade_state_desc");
346 //            }else if(("SUCCESS").equals(result.get("result_code"))){
347 //                return result.get("out_trade_no")+"交易状态:"+result.get("trade_state");
348 //            }else if(("FAIL").equals(result.get("result_code"))){
349 //                return result.get("err_code_des");
350 //            }
351         }else if (("FAIL").equals(result.get("return_code"))){
352             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),result.get("return_msg"));
353         }
354         return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"结果解析错误");
355     }
356 
357     /**
358      * 关闭订单
359      * @param wxCloseOrderModel
360      * @return
361      */
362     @Override
363     public ReturnModel<?> closeOrder(WXCloseOrderModel wxCloseOrderModel) {
364         wxCloseOrderModel.setAppid(WXConstant.APPID);
365         wxCloseOrderModel.setMchId(WXConstant.MCHID);
366         wxCloseOrderModel.setNonceStr(WXPayUtil.getRandomString(32));
367         SortedMap<String,String> reqData = AnnotationUtil.parseObjectToMap(wxCloseOrderModel);
368         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithoutCert(WXConstant.CLOSEORDERURL,reqData);
369         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
370         if(("SUCCESS").equals(result.get("return_code"))){
371             return new ReturnModel<>(result);
372 //            if(("SUCCESS").equals(result.get("result_code"))){
373 //                return "关闭成功";
374 //            }else if(("FAIL").equals(result.get("result_code"))){
375 //                if(("ORDERPAID").equals(result.get("err_code"))){
376 //                    return "订单已支付,不能发起关单,请当作已支付的正常交易";
377 //                }else if(("SYSTEMERROR").equals(result.get("err_code"))){
378 //                    return "系统异常,请重新调用";
379 //                }else if(("ORDERCLOSED").equals(result.get("err_code"))){
380 //                    return "订单已关闭,无需继续调用";
381 //                }else if(("SIGNERROR").equals(result.get("err_code"))){
382 //                    return "请检查签名参数和方法是否都符合签名算法要求";
383 //                }else if(("REQUIRE_POST_METHOD").equals(result.get("err_code"))){
384 //                    return "请检查请求参数是否通过post方法提交";
385 //                }else if(("REQUIRE_POST_METHOD").equals(result.get("err_code"))){
386 //                    return "请检查XML参数格式是否正确";
387 //                }
388 //            }
389         }else if(("FAIL").equals(result.get("return_code"))){
390             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),result.get("return_msg"));
391         }
392         return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"结果解析错误");
393     }
394 
395     /**
396      * 申请退款
397      * @param wxRefundModel
398      * @return
399      */
400     @Override
401     public ReturnModel<?> refund(WXRefundModel wxRefundModel) {
402         wxRefundModel.setSubAppid(WXConstant.SUBAPPID);
403         wxRefundModel.setAppid(WXConstant.APPID);
404         wxRefundModel.setMchId(WXConstant.MCHID);
405         wxRefundModel.setNonceStr(WXPayUtil.getRandomString(32));
406         wxRefundModel.setNotifyUrl(WXConstant.NOTIFYURL);
407         wxRefundModel.setSignType(WXConstant.SIGNTTYPE);
408         SortedMap<String,String> reqData = AnnotationUtil.parseObjectToMap(wxRefundModel);
409         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithCert(WXConstant.REFUNDURL,
410                 reqData,WXConstant.CERTPATH);
411         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
412         if(("SUCCESS").equals(result.get("return_code"))){
413             return new ReturnModel<>(result);
414 //            if(("SUCCESS").equals(result.get("result_code"))){
415 //                return "申请退款成功";
416 //            }else if(("FAIL").equals(result.get("result_code"))){
417 //                return result.get("err_code_des");
418 //            }
419         }else if(("FAIL").equals(result.get("return_code"))){
420             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),result.get("return_msg"));
421         }
422         return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"结果解析错误");
423     }
424 
425     /**
426      * 查询退款(微信退款id查询,只查一笔)
427      * @param wxRefundQueryModel
428      * @return
429      */
430     @Override
431     public ReturnModel<?> refundQuery(WXRefundQueryModel wxRefundQueryModel) {
432         wxRefundQueryModel.setSubAppid(WXConstant.SUBAPPID);
433         wxRefundQueryModel.setAppid(WXConstant.APPID);
434         wxRefundQueryModel.setMchId(WXConstant.MCHID);
435         wxRefundQueryModel.setSignType(WXConstant.SIGNTTYPE);
436         wxRefundQueryModel.setNonceStr(WXPayUtil.getRandomString(32));
437         SortedMap<String,String> reqData = AnnotationUtil.parseObjectToMap(wxRefundQueryModel);
438         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithoutCert(WXConstant.REFUNDQUERYURL,reqData);
439         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
440         if(("SUCCESS").equals(result.get("return_code"))){
441             return new ReturnModel<>(result);
442 //            if(("SUCCESS").equals(result.get("result_code"))){
443 //                return result.get("refund_status_0");
444 //            }else if(("FAIL").equals(result.get("result_code"))){
445 //                return result.get("err_code_des");
446 //            }
447         }else if(("FAIL").equals(result.get("return_code"))){
448             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),result.get("return_msg"));
449         }
450         return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"结果解析错误");
451     }
452 
453     /**
454      * 修改银行卡号
455      * @param modifyarchives
456      * @return
457      */
458     @Override
459     public ReturnModel<?> modifyArchives(WXModifyArchivesModel modifyarchives) {
460         modifyarchives.setVersion("1.0");
461         modifyarchives.setMchId(WXConstant.MCHID);
462         modifyarchives.setNonceStr(WXPayUtil.getRandomString(32));
463         modifyarchives.setSignType(WXConstant.SIGNTTYPE);
464         String certificatesInfo = getCertficates();
465         if(("fail").equals(certificatesInfo) || ("").equals(certificatesInfo)){
466             return new ReturnModel<>(ErrorCode.SERVER_ERROR.getCode(),"安全证书无法解析");
467         }
468         JSONArray data = JSONObject.parseArray(
469                 JSONObject.parseObject(certificatesInfo).getString("data"));
470         //得到平台系列号
471         String serialNo = JSONObject.parseObject(
472                 JSONObject.toJSONString(data.get(0))).getString("serial_no");
473         //得到用于加密敏感信息的证书原文
474         String cert = WXPayUtil.getWXCert(data);
475         modifyarchives.setCertSn(serialNo);
476         modifyarchives.setAccountNumber(RSAEncryptUtil.rsaEncrypt("6214830195599542",cert));
477 
478         SortedMap<String,String> reqData = AnnotationUtil.parseObjectToMap(modifyarchives);
479         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithCert(WXConstant.MODIFYARCHIVESURL,reqData,WXConstant.CERTPATH);
480         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
481         if("SUCCESS".equals(result.get("return_code"))){
482             return new ReturnModel<>(result);
483 //            if("FAIL".equals(result.get("result_code"))){
484 //                return result.get("err_code");
485 //            }else if ("SUCCESS".equals(result.get("result_code"))){
486 //                return result.get("sub_mch_id")+"商户下的银行卡号修改成功";
487 //            }
488         }else if ("FAIL".equals(result.get("return_code"))){
489             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),result.get("return_msg"));
490         }
491         return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"结果解析错误");
492     }
493 
494     /**
495      * 重新发起提现
496      * @param reautowithdrawbydate
497      * @return
498      */
499     @Override
500     public ReturnModel<?> reAutoWithdrawByDate(WXQueryAutoWithdrawByDate reautowithdrawbydate) {
501         reautowithdrawbydate.setSignType(WXConstant.SIGNTTYPE);
502         reautowithdrawbydate.setMchId(WXConstant.MCHID);
503         reautowithdrawbydate.setNonceStr(WXPayUtil.getRandomString(32));
504         SortedMap<String,String> reqData = AnnotationUtil.parseObjectToMap(reautowithdrawbydate);
505         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithCert(WXConstant.REAUTOWITHDRAWBYDATEURL,
506                 reqData,WXConstant.CERTPATH);
507         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
508         if("SUCCESS".equals(result.get("return_code"))){
509             return new ReturnModel<>(result);
510 //            if ("SUCCESS".equals(result.get("result_code"))){
511 //                return result.get("date")+"\t金额:"+result.get("amount");
512 //            }else if ("FAIL".equals(result.get("result_code"))){
513 //                return result.get("err_code")+result.get("err_code_des");
514 //            }
515         }else if ("FAIL".equals(result.get("return_code"))){
516             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),result.get("return_msg"));
517         }
518         return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"结果解析错误");
519     }
520 
521     /**
522      * 修改商户联系信息
523      * @param modifycontactinfo
524      * @return
525      */
526     @Override
527     public ReturnModel<?> modifyContactInfo(WXModifyContactInfoModel modifycontactinfo) {
528         modifycontactinfo.setVersion("1.0");
529         modifycontactinfo.setMchId(WXConstant.MCHID);
530         modifycontactinfo.setNonceStr(WXPayUtil.getRandomString(32));
531         modifycontactinfo.setSignType(WXConstant.SIGNTTYPE);
532         String certificatesInfo = getCertficates();
533         if(("fail").equals(certificatesInfo) || ("").equals(certificatesInfo)){
534             return new ReturnModel<>(ErrorCode.SERVER_ERROR.getCode(),"安全证书无法解析");
535         }
536         JSONArray data = JSONObject.parseArray(
537                 JSONObject.parseObject(certificatesInfo).getString("data"));
538         //得到平台系列号
539         String serialNo = JSONObject.parseObject(
540                 JSONObject.toJSONString(data.get(0))).getString("serial_no");
541         //得到用于加密敏感信息的证书原文
542         String cert = WXPayUtil.getWXCert(data);
543         modifycontactinfo.setCertSn(serialNo);
544         modifycontactinfo.setMobilePhone(RSAEncryptUtil.rsaEncrypt(modifycontactinfo.getMobilePhone(),cert));
545         modifycontactinfo.setEmail(RSAEncryptUtil.rsaEncrypt(modifycontactinfo.getEmail(),cert));
546         SortedMap<String,String> reqData = AnnotationUtil.parseObjectToMap(modifycontactinfo);
547         String returnStr = WXPayHttpUtil.WXHttpPostXMLWithCert(WXConstant.MODIFYCONTACTINFOURL,
548                 reqData,WXConstant.CERTPATH);
549         Map<String,String> result = WXPayXmlUtil.parseXMLToMap(returnStr);
550         if ("SUCCESS".equals(result.get("return_code"))){
551             return new ReturnModel<>(result);
552 //            if ("SUCCESS".equals(result.get("result_code"))){
553 //                return result.get("sub_mch_id")+"商户修改信息成功";
554 //            }else if ("FAIL".equals(result.get("result_code"))){
555 //                return result.get("err_code")+result.get("err_code_des");
556 //            }
557         }else if ("FAIL".equals(result.get("return_code"))){
558             return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),result.get("return_msg"));
559         }
560         return new ReturnModel<>(ErrorCode.BUSINESS_ERROR.getCode(),"结果解析错误");
561     }
562 }
service实现层
  1 public class WXPayHttpUtil {
  2     private static final Logger LOGGER = LoggerFactory.getLogger(WXPayHttpUtil.class);
  3 
  4     private final static String UTFCHARSET = "UTF-8";
  5     private final static String XMLCONTENT = "text/xml;charset=utf-8";
  6     private final static String FORMCONTENT = "multipart/form-data;charset=utf-8";
  7     /**
  8      * 需证书上传form表单形式接口
  9      * @param url 接口地址
 10      * @param mcdId 商户号即密码
 11      * @param certPath 证书文件位置
 12      * @param builder 表单参数
 13      * @return
 14      */
 15     public static String WXHttpPostFormWithCert(String url, String mcdId, String certPath, MultipartEntityBuilder builder){
 16         HttpPost uploadFile = new HttpPost(url);
 17         SSLConnectionSocketFactory sslsf = null;
 18         StringBuilder stringBuilder = new StringBuilder();
 19         //读取本机存放的PKCS12证书文件
 20         ClassLoader classLoader = WXPayHttpUtil.class.getClassLoader();
 21         URL resource = classLoader.getResource(certPath);
 22         try(FileInputStream pkcfile = new FileInputStream(new File(resource.getPath()));
 23         ){
 24             //指定读取证书格式为PKCS12
 25             KeyStore keyStore = KeyStore.getInstance("PKCS12");
 26             //"100000"为密码即商户号
 27             keyStore.load(pkcfile,  mcdId.toCharArray());
 28             SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore,  mcdId.toCharArray()).build();
 29             //指定TLS版本
 30             sslsf = new SSLConnectionSocketFactory(sslcontext);
 31         } catch (Exception e) {
 32             LOGGER.debug(e.getMessage());
 33             return "";
 34         }
 35         HttpEntity multipart = builder.build();
 36         uploadFile.setEntity(multipart);
 37         uploadFile.setHeader("Content-Type", FORMCONTENT);
 38         try(CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
 39             CloseableHttpResponse response = client.execute(uploadFile);)
 40         {
 41             if(response != null && response.getEntity() != null){
 42                 HttpEntity entity = response.getEntity();
 43                 try (
 44                         InputStream in = entity.getContent();
 45                         BufferedReader reader = new BufferedReader(new InputStreamReader(in,"UTF-8")))
 46                 {
 47                     reader.lines().forEach(line->{
 48                         stringBuilder.append(line);
 49                     });
 50                 }
 51             }
 52         } catch (IOException e) {
 53             LOGGER.debug(e.getMessage());
 54             return "";
 55         }
 56 
 57 
 58         return stringBuilder.toString();
 59     }
 60 
 61     /**
 62      * 不需证书上传xml形式接口
 63      * @param url   接口地址
 64      * @param reqDataTo  请求参数
 65      * @return
 66      */
 67     public static String WXHttpPostXMLWithoutCert(String url,SortedMap<String, String> reqDataTo){
 68         HttpPost httpPost = new HttpPost(url);
 69         StringEntity postEntity = new StringEntity(WXPayXmlUtil.parseMapToXML(reqDataTo), UTFCHARSET);
 70         httpPost.setHeader("Content-Type", XMLCONTENT);
 71         httpPost.setEntity(postEntity);
 72         StringBuilder stringBuilder = new StringBuilder();
 73         try(CloseableHttpClient client = HttpClients.createDefault();
 74             CloseableHttpResponse response = client.execute(httpPost);)
 75         {if(response != null && response.getEntity() != null){
 76             HttpEntity entity = response.getEntity();
 77             try (
 78                     InputStream in = entity.getContent();
 79                     BufferedReader reader = new BufferedReader(new InputStreamReader(in,"UTF-8")))
 80             {
 81                 reader.lines().forEach(line->{
 82                     stringBuilder.append(line);
 83                 });;
 84             }
 85         }
 86         } catch (IOException e) {
 87             LOGGER.debug(e.getMessage());
 88             return "";
 89         }
 90         return stringBuilder.toString();
 91     }
 92 
 93     /**
 94      * 需证书上传xml形式接口
 95      * @param url 接口地址
 96      * @param reqData 参数
 97      * @param certPath 证书文件位置
 98      * @return
 99      */
100     public static String WXHttpPostXMLWithCert(String url,SortedMap<String, String> reqData,String certPath){
101         HttpPost queryState = new HttpPost(url);
102         SSLConnectionSocketFactory sslsf = null;
103         //读取本机存放的PKCS12证书文件
104         ClassLoader classLoader = ConfigTool.class.getClassLoader();
105         URL resource = classLoader.getResource(certPath);
106         try(FileInputStream pkcfile = new FileInputStream(new File(resource.getPath()));
107         ){
108             //指定读取证书格式为PKCS12
109             KeyStore keyStore = KeyStore.getInstance("PKCS12");
110             //"100000"为密码即商户号
111             keyStore.load(pkcfile,  reqData.get("mch_id").toCharArray());
112             SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(
113                     keyStore,  reqData.get("mch_id").toCharArray()).build();
114             sslsf = new SSLConnectionSocketFactory(sslcontext,
115                     new String[]{"TLSv1"},
116                     null,
117                     new DefaultHostnameVerifier());
118         } catch (Exception e) {
119             LOGGER.debug(e.getMessage());
120             return "";
121         }
122         StringBuilder stringBuilder = new StringBuilder();
123         StringEntity postEntity = new StringEntity(WXPayXmlUtil.parseMapToXML(reqData), "UTF-8");
124         queryState.setEntity(postEntity);
125         queryState.setHeader("Content-Type", XMLCONTENT);
126         try(CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
127             CloseableHttpResponse response = client.execute(queryState);)
128         {
129             if(response != null && response.getEntity() != null){
130                 HttpEntity entity = response.getEntity();
131                 try (
132                         InputStream in = entity.getContent();
133                         BufferedReader reader = new BufferedReader(
134                                 new InputStreamReader(in,"UTF-8")))
135                 {
136                     reader.lines().forEach(line->{
137                         stringBuilder.append(line);
138                     });
139                 }
140             }
141         } catch (IOException e) {
142             LOGGER.debug(e.getMessage());
143             return "";
144         }
145         return stringBuilder.toString();
146     }
147 
148 }
WXPayHttpUtil
 1 public class WXPayUtil {
 2     private static final String ALGORITHM = "AES/GCM/NoPadding";
 3     private static final int TAG_LENGTH_BIT = 128;
 4     private static final String AES_KEY = "9bbbd2223e5240bb2252d05222210c27"; // APIv3密钥
 5 
 6     /**
 7      * 
 8      * @param aad
 9      * @param iv
10      * @param cipherText
11      * @return
12      * @throws Exception
13      */
14     private static String aesgcmDecrypt(String aad, String iv, String cipherText){
15         try {
16             final Cipher cipher = Cipher.getInstance(ALGORITHM, "SunJCE");
17             SecretKeySpec key = new SecretKeySpec(AES_KEY.getBytes(), "AES");
18             GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, iv.getBytes());
19             cipher.init(Cipher.DECRYPT_MODE, key, spec);
20             cipher.updateAAD(aad.getBytes());
21             return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)));
22         } catch (Exception e) {
23             return "fail";
24         }
25     }
26     public static String getWXCert(JSONArray data) {
27         //敏感信息证书生成所需参数
28         String encryptCertificate = JSONObject.parseObject(
29                 JSONObject.toJSONString(data.get(0))).getString("encrypt_certificate");
30         String nonce = JSONObject.parseObject(
31                 encryptCertificate).getString("nonce");
32         String associatedData = JSONObject.parseObject(
33                 encryptCertificate).getString("associated_data");
34         //要被解密的证书字符串
35         String cipherText = JSONObject.parseObject(
36                 encryptCertificate).getString("ciphertext");
37         String wechatpayCert = "";
38         try {
39             wechatpayCert = aesgcmDecrypt(associatedData, nonce, cipherText);
40         } catch (Exception e) {
41             return wechatpayCert;
42         }
43         return wechatpayCert;
44     }
45     /**
46      * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
47      *
48      * @param data 待签名数据
49      * @param key API密钥
50      * @param signType 签名方式
51      * @return 签名
52      */
53     public static String generateSignature(final Map<String, String> data, String key, String signType) {
54         Set<String> keySet = data.keySet();
55         String[] keyArray = keySet.toArray(new String[keySet.size()]);
56         Arrays.sort(keyArray);
57         StringBuilder sb = new StringBuilder();
58         for (String k : keyArray) {
59             if (k.equals("sign")) {
60                 continue;
61             }
62             if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
63                 sb.append(k).append("=").append(data.get(k).trim()).append("&");
64         }
65         sb.append("key=").append(key);
66         if ("MD5".equals(signType)) {
67             return MD5.getMD5(sb.toString()).toUpperCase();
68         }
69         else if ("HMACSHA256".equals(signType)) {
70             return HMACSHAUtil.getHMACSHA256(sb.toString(), key);
71         }
72         return signType;
73     }
74     //获取指定位数的随机字符串(包含小写字母、大写字母、数字,0<length)
75     public static String getRandomString(int length) {
76         //随机字符串的随机字符库
77         String KeyString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
78         StringBuffer sb = new StringBuffer(); int len = KeyString.length();
79         for (int i = 0; i < length; i++) {
80             sb.append(KeyString.charAt((int) Math.round(Math.random() * (len - 1))));
81         }
82         return sb.toString();
83     }
WXPayUtil
  1 public final class WXPayXmlUtil {
  2     public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
  3         DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
  4         documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
  5         documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
  6         documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
  7         documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
  8         documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
  9         documentBuilderFactory.setXIncludeAware(false);
 10         documentBuilderFactory.setExpandEntityReferences(false);
 11 
 12         return documentBuilderFactory.newDocumentBuilder();
 13     }
 14 
 15     public static Document newDocument() throws ParserConfigurationException {
 16         return newDocumentBuilder().newDocument();
 17     }
 18 
 19     /**
 20      * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
 21      *
 22      * @param strXML
 23      * @return
 24      */
 25     public static Map parseXMLToMap(String strXML) {
 26         Map<String, String> data = new HashMap<String, String>();
 27         try {
 28             DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
 29             InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
 30             Document doc = documentBuilder.parse(stream);
 31             doc.getDocumentElement().normalize();
 32             NodeList nodeList = doc.getDocumentElement().getChildNodes();
 33             for (int idx = 0; idx < nodeList.getLength(); ++idx) {
 34                 Node node = nodeList.item(idx);
 35                 if (node.getNodeType() == Node.ELEMENT_NODE) {
 36                     org.w3c.dom.Element element = (org.w3c.dom.Element) node;
 37                     data.put(element.getNodeName(), element.getTextContent());
 38                 }
 39             }
 40             try {
 41                 stream.close();
 42             } catch (Exception ex) {
 43                 return data;
 44             }
 45         } catch (Exception ex) {
 46                 return data;
 47         }
 48         return data;
 49     }
 50 
 51     /**
 52      * map转xml
 53      * @param data
 54      * @return
 55      */
 56     public static String parseMapToXML(SortedMap<String, String> data) {
 57 //        StringBuffer sb = new StringBuffer();
 58 //        sb.append("<xml>");
 59 //        Set es = parameters.entrySet();
 60 //        Iterator it = es.iterator();
 61 //        while (it.hasNext()) {
 62 //            Map.Entry entry = (Map.Entry)it.next();
 63 //            String k = (String)entry.getKey();
 64 //            String v = (String)entry.getValue();
 65 //            if (null != v && !"".equals(v)) {
 66 //                sb.append("<" + k + ">" + parameters.get(k) + "</" + k + ">\n");
 67 //            }
 68 //        }
 69 //        sb.append("</xml>");
 70 //        return sb.toString();
 71 //    }
 72         Document document = null;
 73         try {
 74             document = WXPayXmlUtil.newDocument();
 75         } catch (ParserConfigurationException e) {
 76             e.printStackTrace();
 77         }
 78         org.w3c.dom.Element root = document.createElement("xml");
 79         document.appendChild(root);
 80         for (String key: data.keySet()) {
 81             String value = data.get(key);
 82             if (value == null) {
 83                 value = "";
 84             }
 85             value = value.trim();
 86             if(!("").equals(value)){
 87                 org.w3c.dom.Element filed = document.createElement(key);
 88                 filed.appendChild(document.createTextNode(value));
 89                 root.appendChild(filed);
 90             }
 91 
 92         }
 93         TransformerFactory tf = TransformerFactory.newInstance();
 94         Transformer transformer = null;
 95         StringWriter writer = new StringWriter();
 96         String output = "";
 97         try {
 98             transformer = tf.newTransformer();
 99             DOMSource source = new DOMSource(document);
100             transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
101             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
102             StreamResult result = new StreamResult(writer);
103             transformer.transform(source, result);
104             output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
105         } catch (Exception e) {
106             return output;
107         }finally {
108             try {
109                 writer.close();
110             } catch (IOException e) {
111                 return output;
112             }
113         }
114         return output;
115     }
116 }
WXPayXmlUtil
 1 public class RSAEncryptUtil {
 2     private static final String CIPHER_PROVIDER = "SunJCE";
 3     private static final String TRANSFORMATION_PKCS1Paddiing = "RSA/ECB/PKCS1Padding";
 4     private static final String CHAR_ENCODING = "UTF-8";//固定值,无须修改
 5     //数据加密方法
 6     private static byte[] encryptPkcs1padding(PublicKey publicKey, byte[] data) throws Exception {
 7         Cipher ci = Cipher.getInstance(TRANSFORMATION_PKCS1Paddiing, CIPHER_PROVIDER);
 8         ci.init(Cipher.ENCRYPT_MODE, publicKey);
 9         return ci.doFinal(data);
10     }
11     //加密后的秘文,使用base64编码方法
12     private static String encodeBase64(byte[] bytes) throws Exception {
13         return Base64.getEncoder().encodeToString(bytes);
14     }
15     //对敏感内容(入参Content)加密,其中PUBLIC_KEY_FILENAME为存放平台证书的路径,平台证书文件存放明文平台证书内容,且为pem格式的平台证书(平台证书的获取方式参照平台证书及序列号获取接口,通过此接口得到的参数certificates包含了加密的平台证书内容ciphertext,然后根据接口文档中平台证书解密指引,最终得到明文平台证书内容)
16      public static String rsaEncrypt(String Content,String cert){
17          final byte[] PublicKeyBytes = cert.getBytes();
18          X509Certificate certificate = null;
19          try {
20              certificate = X509Certificate.getInstance(PublicKeyBytes);
21          } catch (CertificateException e) {
22              return "";
23          }
24          PublicKey publicKey = certificate.getPublicKey();
25          try {
26              return encodeBase64(encryptPkcs1padding(publicKey, Content.getBytes(CHAR_ENCODING)));
27          } catch (Exception e) {
28              return "";
29          }
30      }
31 }
RSAEncryptUtil
  1 public class MD5 {
  2 
  3     /**
  4      * 签名字符串
  5      *
  6      * @param text
  7      *            需要签名的字符串
  8      * @param key
  9      *            密钥
 10      * @param input_charset
 11      *            编码格式
 12      * @return 签名结果
 13      */
 14     public static String sign(String text, String key, String charset) throws Exception {
 15         text = text + key;
 16         return DigestUtils.md5Hex(getContentBytes(text, charset));
 17     }
 18 
 19     /**
 20      * 签名字符串
 21      *
 22      * @param text
 23      *            需要签名的字符串
 24      * @param sign
 25      *            签名结果
 26      * @param key
 27      *            密钥
 28      * @param input_charset
 29      *            编码格式
 30      * @return 签名结果
 31      */
 32     public static boolean verify(String text, String sign, String key, String charset) throws Exception {
 33         text = text + key;
 34         String mysign = DigestUtils.md5Hex(getContentBytes(text, charset));
 35         if (mysign.equals(sign)) {
 36             return true;
 37         } else {
 38             return false;
 39         }
 40     }
 41 
 42     /**
 43      * @param content
 44      * @param charset
 45      * @return
 46      * @throws SignatureException
 47      * @throws UnsupportedEncodingException
 48      */
 49     private static byte[] getContentBytes(String content, String charset) {
 50         if (charset == null || "".equals(charset)) {
 51             return content.getBytes();
 52         }
 53         try {
 54             return content.getBytes(charset);
 55         } catch (UnsupportedEncodingException e) {
 56             throw new RuntimeException("签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
 57         }
 58     }
 59     /**
 60      * 对文件进行MD5获取其Hash值
 61      *
 62      * @param fis
 63      * @return
 64      */
 65     public static String md5HashCode(InputStream fis) {
 66         try {
 67             MessageDigest MD5 = MessageDigest.getInstance("MD5");
 68             byte[] buffer = new byte[8192];
 69             int length;
 70             while ((length = fis.read(buffer)) != -1) {
 71                 MD5.update(buffer, 0, length);
 72             }
 73             return new String(Hex.encodeHex(MD5.digest()));
 74         } catch (Exception e) {
 75             return "";
 76         }
 77     }
 78 
 79     public static String md5HashCode(byte[] fis) {
 80         try {
 81             MessageDigest MD5 = MessageDigest.getInstance("MD5");
 82             MD5.update(fis, 0, fis.length);
 83             return new String(Hex.encodeHex(MD5.digest()));
 84         } catch (Exception e) {
 85             return "";
 86         }
 87     }
 88 
 89     /**
 90      * 生成 MD5
 91      *
 92      * @param data 待处理数据
 93      * @return MD5结果
 94      */
 95     public static String getMD5(String data) {
 96         MessageDigest md;
 97         byte[] array = new byte[0];
 98         try {
 99             md = MessageDigest.getInstance("MD5");
100             array = md.digest(data.getBytes("UTF-8"));
101         } catch (NoSuchAlgorithmException e) {
102             return "";
103         } catch (UnsupportedEncodingException e) {
104             return "";
105         }
106         StringBuilder sb = new StringBuilder();
107         for (byte item : array) {
108             sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
109         }
110         return sb.toString().toUpperCase();
111     }
112 }
MD5
 1 public class AnnotationUtil {
 2     /**
 3      * object转map
 4      * @param
 5      * @return
 6      */
 7     public static SortedMap<String, String> parseObjectToMap(Object obj){
 8         ArrayList<Field> fileds = new ArrayList<>();
 9         Class<?> objClass = obj.getClass();
10         fileds.addAll(Arrays.asList(objClass.getDeclaredFields()));
11         if(objClass.getSuperclass()!=null){
12             fileds.addAll(Arrays.asList(objClass.getSuperclass().getDeclaredFields()));
13         }
14         SortedMap<String,String> map = new TreeMap<>();
15         fileds.forEach(filed->{
16             filed.setAccessible(true);
17             Annotation annotations = filed.getAnnotation(WXPayParam.class);
18             String k = ((WXPayParam) annotations).value();
19             try {
20                String v =  String.valueOf(filed.get(obj));
21                if (null != v && !"".equals(v) && !"null".equals(v)) {
22                    map.put(k,v);
23                }
24             } catch (IllegalAccessException e) {
25                 return;
26             }
27         });
28         if(WXConstant.SIGNTTYPE.equals(map.get("sign_type"))){
29             map.put("sign",WXPayUtil.generateSignature(map,WXConstant.KEY,WXConstant.HMACSHA256TYPE));
30         }else{
31             map.put("sign",WXPayUtil.generateSignature(map,WXConstant.KEY,WXConstant.MD5TYPE));
32         }
33         return map;
34     }
35 }
AnnotationUtil
 1 public class HMACSHAUtil {
 2     /**
 3      * 生成 HMACSHA256
 4      * @param data 待处理数据
 5      * @param key 密钥
 6      * @return 加密结果
 7      * @throws Exception
 8      */
 9     public static String getHMACSHA256(String data, String key){
10         Mac sha256_HMAC;
11         byte[] array = null ;
12         try {
13             sha256_HMAC = Mac.getInstance("HmacSHA256");
14             SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
15             sha256_HMAC.init(secret_key);
16             array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
17         } catch (Exception e) {
18             return "";
19         }
20         StringBuilder sb = new StringBuilder();
21         for (byte item : array) {
22             sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
23         }
24         return sb.toString().toUpperCase();
25     }
26 }
HMACSHAUtil

 六、接口学习总结

    微信接口可以说是相当标准的API文档,说明也比较详细但是对参数的说明不是很准确,导致自己在连接接口的时候看不懂什么意思,一顿询问相关人员参数的含义,希望自己连接微信接口后对于其他的接口也会轻车熟路,一定要细心每一个参数的配置、顺序就没多大问题

 ps:关注一下本人公众号,每周都有新更新哦!

有问题的话,博客回复太慢,可以加一下本人微信:请备注问题!谢谢

 

 

posted @ 2019-02-27 19:01  努力的小雨  阅读(7870)  评论(0编辑  收藏  举报