微信小微商戶直連接口詳解


項目背景:

       小微商戶根據接口申請入駐、查看狀態、進行簽約。商戶信息配置、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:關注一下本人公眾號,每周都有新更新哦!

有問題的話,博客回復太慢,可以加一下本人微信:請備注問題!謝謝

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM