微信小微商户直连接口详解
项目背景:
小微商户根据接口申请入驻、查看状态、进行签约。商户信息配置、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 classWeiXinAccountController {2 @Resource3 WeiXinAccountService weiXinAccountService;4 /** 5 * 上传图片的序列id6 *@return状态7 */ 8 public ReturnModel<?>getPhotoid(@RequestBody WXPhotoModel wxPhotoModel){9 ReturnModel returnModel =weiXinAccountService.getPhotoid(wxPhotoModel);10 returnreturnModel;11 }12 13 /** 14 * 查询审核状态15 *@return状态16 */ 17 public ReturnModel<?>queryForApply(@RequestBody WXApplyStateModel wxApplyStateModel){18 ReturnModel returnModel =weiXinAccountService.queryForApply(wxApplyStateModel);19 returnreturnModel;20 }21 22 /** 23 * 提交参数进行审核24 *@paramwxApplyForEntryModel 参数25 *@return是否提交成功//商户存在时不可编辑(不可再次提交)26 */ 27 public ReturnModel<?>submitInfo(@RequestBody WXApplyForEntryModel wxApplyForEntryModel){28 returnweiXinAccountService.submitInfo(wxApplyForEntryModel);29 }30 31 /***32 * 绑定AppID配置33 *@paramwxAddSubDevConfigModel34 *@throws 35 */ 36 public ReturnModel<?>addSubDevConfig(@RequestBody WXAddSubDevConfigModel wxAddSubDevConfigModel){37 returnweiXinAccountService.addSubDevConfig(wxAddSubDevConfigModel);38 }39 40 /** 41 * 统一下单native42 *@paramwxUnifiedOrderModel43 *@return 44 */ 45 public ReturnModel<?>unifiedOrderByNATIVE(@RequestBody WXUnifiedOrderModel wxUnifiedOrderModel, HttpServletRequest httpServletRequest){46 String ip =WXHttpUtil.getSpbillCreateIp(httpServletRequest);47 wxUnifiedOrderModel.setSpbillCreateIp(ip);48 returnweiXinAccountService.unifiedOrderByNATIVE(wxUnifiedOrderModel);49 }50 /** 51 * 统一下单jsapi52 *@paramwxUnifiedOrderModel53 *@return 54 */ 55 public ReturnModel<?>unifiedOrderByJSAPI(@RequestBody WXUnifiedOrderModel wxUnifiedOrderModel, HttpServletRequest httpServletRequest){56 String ip =WXHttpUtil.getSpbillCreateIp(httpServletRequest);57 wxUnifiedOrderModel.setSpbillCreateIp(ip);58 returnweiXinAccountService.unifiedOrderByJSAPI(wxUnifiedOrderModel);59 }60 /** 61 * 查询订单62 *@paramwxOrderQueryModel63 *@return 64 */ 65 public ReturnModel<?>orderQuery(@RequestBody WXOrderQueryModel wxOrderQueryModel){66 returnweiXinAccountService.orderQuery(wxOrderQueryModel);67 }68 /** 69 * 关闭订单,如果支付失败则需要关闭订单避免重复支付或超时系统退出不再受理也需要关闭70 *@paramwxCloseOrderModel71 *@return 72 */ 73 public ReturnModel<?>closeOrder(@RequestBody WXCloseOrderModel wxCloseOrderModel){74 returnweiXinAccountService.closeOrder(wxCloseOrderModel);75 }76 77 /** 78 * 申请退款79 *@paramwxRefundModel80 *@return 81 */ 82 public ReturnModel<?>refund(@RequestBody WXRefundModel wxRefundModel){83 returnweiXinAccountService.refund(wxRefundModel);84 }85 86 /** 87 * 查询退款88 *@paramwxRefundQueryModel89 *@return 90 */ 91 public ReturnModel<?>refundQuery(@RequestBody WXRefundQueryModel wxRefundQueryModel){92 returnweiXinAccountService.refundQuery(wxRefundQueryModel);93 }94 95 /** 96 * 提现状态查询97 *@paramwxQueryAutoWithdrawByDate98 *@return 99 */ 100 public ReturnModel<?>queryAutoWithdrawByDate(@RequestBody WXQueryAutoWithdrawByDate wxQueryAutoWithdrawByDate){101 returnweiXinAccountService.queryAutoWithdrawByDate(wxQueryAutoWithdrawByDate);102 }103 104 /** 105 * 修改银行卡号106 *@parammodifyarchives107 *@return 108 *@throwsException109 */ 110 public ReturnModel<?> modifyArchives(@RequestBody WXModifyArchivesModel modifyarchives) throwsException {111 returnweiXinAccountService.modifyArchives(modifyarchives);112 }113 114 /** 115 * 重新发起提现116 *@paramreautowithdrawbydate117 *@return 118 */ 119 public ReturnModel<?>reAutoWithdrawByDate(@RequestBody WXQueryAutoWithdrawByDate reautowithdrawbydate) {120 returnweiXinAccountService.reAutoWithdrawByDate(reautowithdrawbydate);121 }122 123 /** 124 * 修改商户联系信息125 *@parammodifycontactinfo126 *@return 127 *128 *@throwsException129 */ 130 public ReturnModel<?> modifyContactInfo(@RequestBody WXModifyContactInfoModel modifycontactinfo) throwsException {131 returnweiXinAccountService.modifyContactInfo(modifycontactinfo);132 }133 }
WXController
1 public class WeiXinAccountServiceImpl implementsWeiXinAccountService {2 private final Logger LOGGER = LoggerFactory.getLogger(WeiXinAccountServiceImpl.class);3 /** 4 * 准备申请入驻的参数5 *@return入驻的参数6 *@throwsException7 */ 8 public SortedMap<String, String> applyForEntry(WXApplyForEntryModel wxApplyForEntryModel) throwsException {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(newDate().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 returnreqPara;39 }40 /** 41 * 得到平台证书包含的参数json42 *@return 43 */ 44 publicString 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 * 得到上传照片的id60 *@paramwxPhotoModel 图片61 *@return图片id62 */ 63 @Override64 public ReturnModel<?>getPhotoid(WXPhotoModel wxPhotoModel){65 String cent = "";66 InputStream fileInputStream = null;67 DataInputStream dis = null;68 byte[] bufferOut;69 try{70 URL url = newURL(wxPhotoModel.getFileUrlPath());71 HttpURLConnection httpUrl =(HttpURLConnection) url.openConnection();72 httpUrl.setDoInput(true);73 httpUrl.setRequestMethod("GET");74 fileInputStream =httpUrl.getInputStream();75 dis = newDataInputStream(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 @Override124 public ReturnModel<?>unifiedOrderByNATIVE(WXUnifiedOrderModel wxUnifiedOrderModel) {125 wxUnifiedOrderModel.setProductId(WXPayUtil.getRandomString(32));126 wxUnifiedOrderModel.setTradeType("NATIVE");127 returnunifiedOrder(wxUnifiedOrderModel);128 }129 130 @Override131 public ReturnModel<?>unifiedOrderByJSAPI(WXUnifiedOrderModel wxUnifiedOrderModel) {132 wxUnifiedOrderModel.setSubAppid(WXConstant.SUBAPPID);133 wxUnifiedOrderModel.setTradeType("JSAPI");134 returnunifiedOrder(wxUnifiedOrderModel);135 }136 137 /** 138 * 查询审核状态139 *@paramapplyState 参数-商户号、服务商自定义的用户编号140 *@return 141 */ 142 @Override143 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 // //审核结果json163 //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 *@paramwxApplyForEntryModel 请求商户信息187 *@return 188 */ 189 @Override190 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 *@paramwxQueryAutoWithdrawByDate226 *@return 227 */ 228 @Override229 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 *@paramwxUnifiedOrderModel262 *@return 263 */ 264 @Override265 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(newDate().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 *@paramwxAddSubDevConfigModel304 *@return 305 */ 306 @Override307 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 *@paramwxOrderQueryModel330 *@return 331 */ 332 @Override333 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 *@paramwxCloseOrderModel360 *@return 361 */ 362 @Override363 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 *@paramwxRefundModel398 *@return 399 */ 400 @Override401 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 *@paramwxRefundQueryModel428 *@return 429 */ 430 @Override431 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 *@parammodifyarchives456 *@return 457 */ 458 @Override459 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 *@paramreautowithdrawbydate497 *@return 498 */ 499 @Override500 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 *@parammodifycontactinfo524 *@return 525 */ 526 @Override527 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 classWXPayHttpUtil {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 *@paramurl 接口地址10 *@parammcdId 商户号即密码11 *@paramcertPath 证书文件位置12 *@parambuilder 表单参数13 *@return 14 */ 15 public staticString WXHttpPostFormWithCert(String url, String mcdId, String certPath, MultipartEntityBuilder builder){16 HttpPost uploadFile = newHttpPost(url);17 SSLConnectionSocketFactory sslsf = null;18 StringBuilder stringBuilder = newStringBuilder();19 //读取本机存放的PKCS12证书文件 20 ClassLoader classLoader = WXPayHttpUtil.class.getClassLoader();21 URL resource =classLoader.getResource(certPath);22 try(FileInputStream pkcfile = new FileInputStream(newFile(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 = newSSLConnectionSocketFactory(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 returnstringBuilder.toString();59 }60 61 /** 62 * 不需证书上传xml形式接口63 *@paramurl 接口地址64 *@paramreqDataTo 请求参数65 *@return 66 */ 67 public static String WXHttpPostXMLWithoutCert(String url,SortedMap<String, String>reqDataTo){68 HttpPost httpPost = newHttpPost(url);69 StringEntity postEntity = newStringEntity(WXPayXmlUtil.parseMapToXML(reqDataTo), UTFCHARSET);70 httpPost.setHeader("Content-Type", XMLCONTENT);71 httpPost.setEntity(postEntity);72 StringBuilder stringBuilder = newStringBuilder();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 returnstringBuilder.toString();91 }92 93 /** 94 * 需证书上传xml形式接口95 *@paramurl 接口地址96 *@paramreqData 参数97 *@paramcertPath 证书文件位置98 *@return 99 */ 100 public static String WXHttpPostXMLWithCert(String url,SortedMap<String, String>reqData,String certPath){101 HttpPost queryState = newHttpPost(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(newFile(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 = newSSLConnectionSocketFactory(sslcontext,115 new String[]{"TLSv1"},116 null,117 newDefaultHostnameVerifier());118 } catch(Exception e) {119 LOGGER.debug(e.getMessage());120 return "";121 }122 StringBuilder stringBuilder = newStringBuilder();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 = newBufferedReader(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 returnstringBuilder.toString();146 }147 148 }
WXPayHttpUtil
1 public classWXPayUtil {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 *@paramaad9 *@paramiv10 *@paramcipherText11 *@return 12 *@throwsException13 */ 14 private staticString 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 = newGCMParameterSpec(TAG_LENGTH_BIT, iv.getBytes());19 cipher.init(Cipher.DECRYPT_MODE, key, spec);20 cipher.updateAAD(aad.getBytes());21 return newString(cipher.doFinal(Base64.getDecoder().decode(cipherText)));22 } catch(Exception e) {23 return "fail";24 }25 }26 public staticString 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 returnwechatpayCert;42 }43 returnwechatpayCert;44 }45 /** 46 * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。47 *48 *@paramdata 待签名数据49 *@paramkey API密钥50 *@paramsignType 签名方式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(newString[keySet.size()]);56 Arrays.sort(keyArray);57 StringBuilder sb = newStringBuilder();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 returnMD5.getMD5(sb.toString()).toUpperCase();68 }69 else if ("HMACSHA256".equals(signType)) {70 returnHMACSHAUtil.getHMACSHA256(sb.toString(), key);71 }72 returnsignType;73 }74 //获取指定位数的随机字符串(包含小写字母、大写字母、数字,0<length) 75 public static String getRandomString(intlength) {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 returnsb.toString();83 }
WXPayUtil
1 public final classWXPayXmlUtil {2 public static DocumentBuilder newDocumentBuilder() throwsParserConfigurationException {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 returndocumentBuilderFactory.newDocumentBuilder();13 }14 15 public static Document newDocument() throwsParserConfigurationException {16 returnnewDocumentBuilder().newDocument();17 }18 19 /** 20 * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。21 *22 *@paramstrXML23 *@return 24 */ 25 public staticMap 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 returndata;44 }45 } catch(Exception ex) {46 returndata;47 }48 returndata;49 }50 51 /** 52 * map转xml53 *@paramdata54 *@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 = newStringWriter();96 String output = "";97 try{98 transformer =tf.newTransformer();99 DOMSource source = newDOMSource(document);100 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");101 transformer.setOutputProperty(OutputKeys.INDENT, "yes");102 StreamResult result = newStreamResult(writer);103 transformer.transform(source, result);104 output = writer.getBuffer().toString(); //.replaceAll("\n|\r", ""); 105 } catch(Exception e) {106 returnoutput;107 }finally{108 try{109 writer.close();110 } catch(IOException e) {111 returnoutput;112 }113 }114 returnoutput;115 }116 }
WXPayXmlUtil
1 public classRSAEncryptUtil {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) throwsException {7 Cipher ci =Cipher.getInstance(TRANSFORMATION_PKCS1Paddiing, CIPHER_PROVIDER);8 ci.init(Cipher.ENCRYPT_MODE, publicKey);9 returnci.doFinal(data);10 }11 //加密后的秘文,使用base64编码方法 12 private static String encodeBase64(byte[] bytes) throwsException {13 returnBase64.getEncoder().encodeToString(bytes);14 }15 //对敏感内容(入参Content)加密,其中PUBLIC_KEY_FILENAME为存放平台证书的路径,平台证书文件存放明文平台证书内容,且为pem格式的平台证书(平台证书的获取方式参照平台证书及序列号获取接口,通过此接口得到的参数certificates包含了加密的平台证书内容ciphertext,然后根据接口文档中平台证书解密指引,最终得到明文平台证书内容) 16 public staticString 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 returnencodeBase64(encryptPkcs1padding(publicKey, Content.getBytes(CHAR_ENCODING)));27 } catch(Exception e) {28 return "";29 }30 }31 }
RSAEncryptUtil
1 public classMD5 {2 3 /** 4 * 签名字符串5 *6 *@paramtext7 * 需要签名的字符串8 *@paramkey9 * 密钥10 *@paraminput_charset11 * 编码格式12 *@return签名结果13 */ 14 public static String sign(String text, String key, String charset) throwsException {15 text = text +key;16 returnDigestUtils.md5Hex(getContentBytes(text, charset));17 }18 19 /** 20 * 签名字符串21 *22 *@paramtext23 * 需要签名的字符串24 *@paramsign25 * 签名结果26 *@paramkey27 * 密钥28 *@paraminput_charset29 * 编码格式30 *@return签名结果31 */ 32 public static boolean verify(String text, String sign, String key, String charset) throwsException {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 *@paramcontent44 *@paramcharset45 *@return 46 *@throwsSignatureException47 *@throwsUnsupportedEncodingException48 */ 49 private static byte[] getContentBytes(String content, String charset) {50 if (charset == null || "".equals(charset)) {51 returncontent.getBytes();52 }53 try{54 returncontent.getBytes(charset);55 } catch(UnsupportedEncodingException e) {56 throw new RuntimeException("签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" +charset);57 }58 }59 /** 60 * 对文件进行MD5获取其Hash值61 *62 *@paramfis63 *@return 64 */ 65 public staticString md5HashCode(InputStream fis) {66 try{67 MessageDigest MD5 = MessageDigest.getInstance("MD5");68 byte[] buffer = new byte[8192];69 intlength;70 while ((length = fis.read(buffer)) != -1) {71 MD5.update(buffer, 0, length);72 }73 return newString(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 newString(Hex.encodeHex(MD5.digest()));84 } catch(Exception e) {85 return "";86 }87 }88 89 /** 90 * 生成 MD591 *92 *@paramdata 待处理数据93 *@returnMD5结果94 */ 95 public staticString 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 = newStringBuilder();107 for (byteitem : array) {108 sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));109 }110 returnsb.toString().toUpperCase();111 }112 }
MD5
1 public classAnnotationUtil {2 /** 3 * object转map4 *@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 returnmap;34 }35 }
AnnotationUtil
1 public classHMACSHAUtil {2 /** 3 * 生成 HMACSHA2564 *@paramdata 待处理数据5 *@paramkey 密钥6 *@return加密结果7 *@throwsException8 */ 9 public staticString 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 = newStringBuilder();21 for (byteitem : array) {22 sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));23 }24 returnsb.toString().toUpperCase();25 }26 }
HMACSHAUtil
六、接口学习总结
微信接口可以说是相当标准的API文档,说明也比较详细但是对参数的说明不是很准确,导致自己在连接接口的时候看不懂什么意思,一顿询问相关人员参数的含义,希望自己连接微信接口后对于其他的接口也会轻车熟路,一定要细心每一个参数的配置、顺序就没多大问题
转载于:https://www.cnblogs.com/guoxiaoyu/p/10445972.html
微信小微商户直连接口详解相关推荐
- java小程序详解_微信小程序登录Java后台接口(详解,附示例代码)
首先看一下官方文档 地址:微信小程序官方文档API登录接口 我们先对官方给的时序图进行简单的分析 1.当小程序调用wx.login()时,会获得一个code(临时登录凭证),然后我们需要用wx.req ...
- 小程序setdata优化_微信小程序 setData的使用方法详解
微信小程序 setData的使用方法详解 微信小程序 setData的使用方法详解 最近在使用微信小程序的setData时,遇到了以下问题.如下: 官网文档在使用setData()设置数组对象的某个元 ...
- 微信小程token_微信小程序url与token设置详解
微信小程序url与token设置详解 新浪云应用sae的代码里创建一个weixin.php文件,写入以下代码 isValid(); class wechatAPI { public function ...
- 微信小程序一键置顶操作详解:
微信小程序一键置顶操作详解: 第一种方式:采用scroll-view滚动视图实现 第二种方式,直接用view实现 第一种方式:采用scroll-view滚动视图实现 下面是代码简介: wxml文件代码 ...
- nodejs实现微信支付小微商户申请入驻接口
微信支付小微商户可以通过小程序<微信买单服务商助手>来进件,也可以通过API接口来进件(详情可查阅小微商户专属接口文档). 2种方式进件后的商户是有区别的 不同进件方式下的小微商户对比 ...
- 微信小程序01【目录结构详解、视图与渲染、事件、input、scroll-view】
学习地址:https://www.bilibili.com/video/BV1sx411z77P 笔记01:https://blog.csdn.net/weixin_44949135/article/ ...
- 微信小程序实战须知2:详解数据API所需网络配置域名、Https、备案、SSL
网络通信 小程序请求数据API时比较头疼,不能用想当然方式进行学习. 服务器域名.IP.HTTPS 域名1 域名(Domain Name):又称网域,是由一串用点分隔的名字组成的Internet上某一 ...
- 【联机对战】微信小程序联机游戏开发流程详解
现有一个微信小程序叫中国象棋项目,棋盘类的单机游戏看着有缺少了什么,现在给补上了,加个联机对战的功能,增加了可玩性,对新手来说,实现联机游戏还是有难度的,那要怎么实现的呢,接下来给大家讲一下. 考虑到 ...
- 微信小程序数组网页调用实例详解:李贺作品集
额,我觉得这个东西应该叫做数组,有没有更专业的说法,我实在不知道. 惯例,先看效果,只实现数组存储,点击跳转网页,其他的都省略了. 如图可见,这个小程序有两个页面,1是首页index,2是跳转页det ...
最新文章
- sqlserver 类似oracle的rownum功能: row_number
- Linux下常用 60个命令
- 百雀羚、林清轩、逐本走红背后,植物基护肤品的春天来了?
- Kubernetes中使用CronJob定时备份etcd集群数据
- python中msg函数_Python 知识点考点之闭包
- 图像处理基础(五)_图像像素中通道概念解析
- 【ZOJ】3380 Patchouli's Spell Cards
- 平衡二叉树、B树、B+树、B*树
- MapReduce编程模型简介和总结
- PCIe协议中的电源管理概述
- VMware15 Pro激活密钥
- 线性代数 --- 什么是矩阵的逆?(个人笔记扫描版)
- numpy中的revel和flatten
- 计算机和信息技术革命,人类历史上的四次信息技术革命
- c语言Ox5516,采药 (C语言代码)
- 【git系列005】git分支学习
- 【git之路】拉取远程分支到本地
- P2324 [SCOI2005]骑士精神(IDA*)
- 【For my liz】宇宙制作全纪录(如果能成功的话TT)
- Rust之Sea-orm快速入门指南
热门文章
- IntelliJ IDEA中的神仙插件(写代码必备)
- 观「招商银行」隐私计算布局的思考(36氪收录)
- VMware如此强大,势必成为开源世界的老大
- 测试之第四集找bug的专业与素养
- 基于红外技术的交通灯设计
- 亚马逊视频下载用什么工具:试试专业亚马逊视频下载器-Tuneboto Amazon Video Downloader中文版 | 亚马逊上的视频怎么下载?
- win7设置计算机临时用户,为什么Win7用域账号登录以后总显示为临时配置账户? 穿墙书店...
- wps怎么免费导出简历_求职简历怎么写 个人简历怎么写 简历怎么制作
- 读《轻断食》的一些体会
- STMF4编写系统时钟