微信退款之前需要在常量中配置退款地址,退款的地址必須是可以直接訪問的。(之前的申請商戶平臺及在開放平臺申請賬號不在描述)在調(diào)起之前需要下載商戶平臺上的證書將其放在項目src下。
我們提供的服務(wù)有:成都網(wǎng)站設(shè)計、網(wǎng)站建設(shè)、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認證、林芝ssl等。為1000多家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的林芝網(wǎng)站制作公司
微信退款回調(diào)url :微信官方建議在提交退款申請后進行退款回調(diào)url配置,便于通知退款的結(jié)果。配置在微信商戶平臺-》交易中心-》退款配置欄進行退款結(jié)果回調(diào)通知配置。配置的url必須為可以直接訪問的類似付款成功回調(diào)url。
配置成功后在回調(diào)的url中處理退款后的結(jié)果。微信給返回的參數(shù)如下:
對于加密信息req_info的解密步驟如下。
解密過程中需要先到oracle官網(wǎng)下載對應(yīng)java版本的sercity包,然后將下載后的包中兩個jar復(fù)制到j(luò)dk/jre/lib/security/包下并覆蓋原jar.另外需要在項目中引入一個bcprov-jdk15on-158.jar
退款部分代碼:
// 構(gòu)造向微信發(fā)送參數(shù)的實體類 private Unifiedorder unifiedorder = new Unifiedorder(); // 微信的參數(shù) private WeixinConfigUtils config = new WeixinConfigUtils(); public static final String ALGORITHM = "AES/ECB/PKCS7Padding"; private RefundOrder refundOrder = new RefundOrder(); @Resource private AESDecodeUtil aesDecodeUtil; @Action("weixinRefund") public String weixinRefund() { try { String out_trade_no = getRequest().getParameter("out_trade_no"); String refund_fee = getRequest().getParameter("refund_fee"); aLiPay = aLiPayService.searchALiPayByOutTradeNo(out_trade_no); if (aLiPay != null) { // 參數(shù)組 String appid = aLiPay.getApp_id(); String mch_id = config.mch_id; String nonce_str = RandCharsUtils.getRandomString(16); String out_refund_no = UuIdUtils.getUUID(); Integer total_fee = aLiPay.getTotalFee(); // 構(gòu)造簽名 parameters.put("appid", appid); parameters.put("mch_id", mch_id); parameters.put("nonce_str", nonce_str); parameters.put("out_trade_no", out_trade_no); parameters.put("out_refund_no", out_refund_no); parameters.put("total_fee", total_fee); parameters.put("refund_fee", refund_fee); parameters.put("op_user_id", mch_id); String sign = WXSignUtils.createSign("UTF-8", parameters); // 向微信發(fā)送xml消息 // Unifiedorder unifiedorder = new Unifiedorder(); unifiedorder.setAppid(appid); unifiedorder.setMch_id(mch_id); unifiedorder.setSign(sign); unifiedorder.setNonce_str(nonce_str); unifiedorder.setOut_trade_no(out_trade_no); unifiedorder.setOut_refund_no(out_refund_no); unifiedorder.setTotal_fee(total_fee); unifiedorder.setRefund_fee(Integer.valueOf(refund_fee)); unifiedorder.setOp_user_id(mch_id); String xmlInfo = HttpXmlUtils.refundXml(unifiedorder); unifiedorder = null; try { CloseableHttpResponse response = HttpUtil.Post( weixinConstant.REFUND_URL, xmlInfo, true); // 輸出退款后的信息 String refundXml = EntityUtils.toString(response.getEntity(), "utf-8"); Map<String, String> refundOrderMap = HttpXmlUtils.parseRefundXml(refundXml); if (refundOrderMap.size()>0) { if (refundOrderMap.get("result_code").equals("SUCCESS") && refundOrderMap.get("return_code").equals("SUCCESS")) { refundOrder.setAppid(refundOrderMap.get("appid")); refundOrder.setMch_id(refundOrderMap.get("mch_id")); refundOrder.setTransaction_id(refundOrderMap.get("transaction_id")); refundOrder.setOut_trade_no(refundOrderMap.get("out_trade_no")); refundOrder.setOut_refund_no(refundOrderMap.get("out_refund_no")); refundOrder.setRefund_id(refundOrderMap.get("refund_id")); refundOrder.setRefund_fee(Integer.valueOf(refundOrderMap.get("refund_fee"))); refundOrder.setTatol_fee(Integer.valueOf(refundOrderMap.get("total_fee"))); refundOrder.setRefund_status(WeixinRefundStatusConstant.APPLY_SUCCESS); refundOrderService.saveOrder(refundOrder); msg.put("stateCode", MsgCode.SUCCESS); msg.put("message ", "退款申請成功"); refundOrderService.saveOrder(refundOrder); }else { msg.put("stateCode", MsgCode.ERROR); msg.put("message ", "退款申請失敗"); } }else { msg.put("stateCode", MsgCode.ERROR); msg.put("message ", "退款申請失敗"); } try { // 關(guān)閉流 EntityUtils.consume(response.getEntity()); } finally { response.close(); } } catch (Exception e) { msg.put("stateCode", MsgCode.SYS_ERROR); msg.put("message ", "
package com.wellness.platfront.common.weixinutil; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Random; 生成隨機串 /** * nonce_str隨即字符串 * @author * @date 2017/08/10 */ public class RandCharsUtils { private static SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); public static String getRandomString(int length) { //length表示生成字符串的長度 String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; Random random = new Random(); StringBuffer sb = new StringBuffer(); int number = 0; for (int i = 0; i < length; i++) { number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } /* * 訂單開始交易的時間 */ public static String timeStart(){ return df.format(new Date()); } /* * 訂單開始交易的時間 */ public static String timeExpire(){ Calendar now=Calendar.getInstance(); now.add(Calendar.MINUTE,30); return df.format(now.getTimeInMillis()); } }
生成簽名的類
package com.wellness.platfront.common.weixinutil; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.SortedMap; /** * 微信支付簽名 * @author * @date 2017/08/10 */ public class WXSignUtils { /** * 微信支付簽名算法sign * @param characterEncoding * @param parameters * @return */ @SuppressWarnings("rawtypes") public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();//所有參與傳參的參數(shù)按照accsii排序(升序) Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); Object v = entry.getValue(); if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + weixinConstant.KEY); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } }
向微信發(fā)送消息的類
package com.wellness.platfront.common.weixinutil; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.StringReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Resource; import javax.net.ssl.HttpsURLConnection; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.xml.sax.InputSource; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder; import com.wellness.platfront.business.wechat.service.RefundOrderService; import com.wellness.platfront.entity.weixin.Transfers; import com.wellness.platfront.entity.weixin.Unifiedorder; /** * post提交xml格式的參數(shù) * @author * @date 2017/08/10 */ public class HttpXmlUtils { @Resource private RefundOrderService refundOrderService; public static XStream xStream = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_"))); /** * 開始post提交參數(shù)到接口 * 并接受返回 * @param url * @param xml * @param method * @param contentType * @return */ public static String xmlHttpProxy(String url,String xml,String method,String contentType){ InputStream is = null; OutputStreamWriter os = null; try { URL _url = new URL(url); HttpURLConnection conn = (HttpURLConnection) _url.openConnection(); conn.setDoInput(true); conn.setDoOutput(true); conn.setRequestProperty("Content-type", "text/xml"); conn.setRequestProperty("Pragma:", "no-cache"); conn.setRequestProperty("Cache-Control", "no-cache"); conn.setRequestMethod("POST"); os = new OutputStreamWriter(conn.getOutputStream()); os.write(new String(xml.getBytes(contentType))); os.flush(); //返回值 is = conn.getInputStream(); return getContent(is, "utf-8"); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ try { if(os!=null){os.close();} if(is!=null){is.close();} } catch (IOException e) { e.printStackTrace(); } } return null; } /** * 解析返回的值 * @param is * @param charset * @return */ public static String getContent(InputStream is, String charset) { String pageString = null; InputStreamReader isr = null; BufferedReader br = null; StringBuffer sb = null; try { isr = new InputStreamReader(is, charset); br = new BufferedReader(isr); sb = new StringBuffer(); String line = null; while ((line = br.readLine()) != null) { sb.append(line + "\n"); } pageString = sb.toString(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (is != null){ is.close(); } if(isr!=null){ isr.close(); } if(br!=null){ br.close(); } } catch (IOException e) { e.printStackTrace(); } sb = null; } return pageString; } /** * 解析申請退款之后微信返回的值并進行存庫操作 * @throws IOException * @throws JDOMException */ public static Map<String, String> parseRefundXml(String refundXml) throws JDOMException, IOException{ ParseXMLUtils.jdomParseXml(refundXml); StringReader read = new StringReader(refundXml); // 創(chuàng)建新的輸入源SAX 解析器將使用 InputSource 對象來確定如何讀取 XML 輸入 InputSource source = new InputSource(read); // 創(chuàng)建一個新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通過輸入源構(gòu)造一個Document org.jdom.Document doc; doc = (org.jdom.Document) sb.build(source); org.jdom.Element root = doc.getRootElement();// 指向根節(jié)點 List<org.jdom.Element> list = root.getChildren(); Map<String, String> refundOrderMap = new HashMap<String, String>(); if(list!=null&&list.size()>0){ for (org.jdom.Element element : list) { refundOrderMap.put(element.getName(), element.getText()); } return refundOrderMap; } return null; } /** * 解析申請退款之后微信退款回調(diào)返回的字符串中內(nèi)容 * @throws IOException * @throws JDOMException */ public static Map<String, String> parseRefundNotifyXml(String refundXml) throws JDOMException, IOException{ ParseXMLUtils.jdomParseXml(refundXml); StringReader read = new StringReader(refundXml); // 創(chuàng)建新的輸入源SAX 解析器將使用 InputSource 對象來確定如何讀取 XML 輸入 InputSource source = new InputSource(read); // 創(chuàng)建一個新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通過輸入源構(gòu)造一個Document org.jdom.Document doc; doc = (org.jdom.Document) sb.build(source); org.jdom.Element root = doc.getRootElement();// 指向根節(jié)點 List<org.jdom.Element> list = root.getChildren(); Map<String, String> resultMap = new HashMap<>(); if(list!=null&&list.size()>0){ for (org.jdom.Element element : list){ resultMap.put(element.getName(), element.getText()); } return resultMap; } return null; } /** * h6支付時 解析返回的值并返回prepareid * @throws IOException * @throws JDOMException */ public static Map<String, String> getUrl(Unifiedorder unifiedorder) throws JDOMException, IOException{ String xmlInfo = HttpXmlUtils.xmlH5Info(unifiedorder); String wxUrl = weixinConstant.URL; String method = "POST"; String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString(); ParseXMLUtils.jdomParseXml(weixinPost); StringReader read = new StringReader(weixinPost); // 創(chuàng)建新的輸入源SAX 解析器將使用 InputSource 對象來確定如何讀取 XML 輸入 InputSource source = new InputSource(read); // 創(chuàng)建一個新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通過輸入源構(gòu)造一個Document org.jdom.Document doc; doc = (org.jdom.Document) sb.build(source); org.jdom.Element root = doc.getRootElement();// 指向根節(jié)點 List<org.jdom.Element> list = root.getChildren(); String prepayId =null; Map<String, String> msg = new HashMap<String, String>(); if(list!=null&&list.size()>0){ for (org.jdom.Element element : list) { msg.put(element.getName(), element.getText()); } } return msg; } /** * 解析返回的值并返回prepareid * @throws IOException * @throws JDOMException */ public static String getPrepareId(Unifiedorder unifiedorder) throws JDOMException, IOException{ String xmlInfo = HttpXmlUtils.xmlInfo(unifiedorder); String wxUrl = weixinConstant.URL; String method = "POST"; String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString(); ParseXMLUtils.jdomParseXml(weixinPost); StringReader read = new StringReader(weixinPost); // 創(chuàng)建新的輸入源SAX 解析器將使用 InputSource 對象來確定如何讀取 XML 輸入 InputSource source = new InputSource(read); // 創(chuàng)建一個新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通過輸入源構(gòu)造一個Document org.jdom.Document doc; doc = (org.jdom.Document) sb.build(source); org.jdom.Element root = doc.getRootElement();// 指向根節(jié)點 List<org.jdom.Element> list = root.getChildren(); String prepayId =null; if(list!=null&&list.size()>0){ for (org.jdom.Element element : list) { if ( "prepay_id".equals(element.getName())) { prepayId= element.getText(); break; } } } return prepayId; } /** * 向微信發(fā)送企業(yè)付款請求并解析返回結(jié)果 * @throws IOException * @throws JDOMException */ public static Map<String, String> getTransfersMap(Transfers transfers) throws JDOMException, IOException{ String xmlInfo = HttpXmlUtils.xmlTransfer(transfers); String wxUrl = weixinConstant.WITHDRAW_URL; String method = "POST"; String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString(); ParseXMLUtils.jdomParseXml(weixinPost); StringReader read = new StringReader(weixinPost); // 創(chuàng)建新的輸入源SAX 解析器將使用 InputSource 對象來確定如何讀取 XML 輸入 InputSource source = new InputSource(read); // 創(chuàng)建一個新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通過輸入源構(gòu)造一個Document org.jdom.Document doc; doc = (org.jdom.Document) sb.build(source); org.jdom.Element root = doc.getRootElement();// 指向根節(jié)點 List<org.jdom.Element> list = root.getChildren(); Map<String, String> transferMap=new HashMap<>(); if(list!=null&&list.size()>0){ for (org.jdom.Element element : list) { transferMap.put(element.getName(), element.getText()); } } return transferMap; } /** * 構(gòu)造退款xml參數(shù) * @param xml * @return */ public static String refundXml(Unifiedorder unifiedorder){ xStream.autodetectAnnotations(true); xStream.alias("xml", Unifiedorder.class); return xStream.toXML(unifiedorder); } /** * 構(gòu)造企業(yè)付款xml參數(shù) * @param xml * @return */ public static String transferXml(Transfers transfers){ xStream.autodetectAnnotations(true); xStream.alias("xml", Transfers.class); return xStream.toXML(transfers); } /** * 構(gòu)造xml參數(shù) * @param xml * @return */ public static String xmlInfo(Unifiedorder unifiedorder){ if(unifiedorder!=null){ StringBuffer bf = new StringBuffer(); bf.append("<xml>"); bf.append("<appid><![CDATA["); bf.append(unifiedorder.getAppid()); bf.append("]]></appid>"); bf.append("<mch_id><![CDATA["); bf.append(unifiedorder.getMch_id()); bf.append("]]></mch_id>"); bf.append("<nonce_str><![CDATA["); bf.append(unifiedorder.getNonce_str()); bf.append("]]></nonce_str>"); bf.append("<sign><![CDATA["); bf.append(unifiedorder.getSign()); bf.append("]]></sign>"); bf.append("<body><![CDATA["); bf.append(unifiedorder.getBody()); bf.append("]]></body>"); bf.append("<detail><![CDATA["); bf.append(unifiedorder.getDetail()); bf.append("]]></detail>"); bf.append("<attach><![CDATA["); bf.append(unifiedorder.getAttach()); bf.append("]]></attach>"); bf.append("<out_trade_no><![CDATA["); bf.append(unifiedorder.getOut_trade_no()); bf.append("]]></out_trade_no>"); bf.append("<total_fee><![CDATA["); bf.append(unifiedorder.getTotal_fee()); bf.append("]]></total_fee>"); bf.append("<spbill_create_ip><![CDATA["); bf.append(unifiedorder.getSpbill_create_ip()); bf.append("]]></spbill_create_ip>"); bf.append("<time_start><![CDATA["); bf.append(unifiedorder.getTime_start()); bf.append("]]></time_start>"); bf.append("<time_expire><![CDATA["); bf.append(unifiedorder.getTime_expire()); bf.append("]]></time_expire>"); bf.append("<notify_url><![CDATA["); bf.append(unifiedorder.getNotify_url()); bf.append("]]></notify_url>"); bf.append("<trade_type><![CDATA["); bf.append(unifiedorder.getTrade_type()); bf.append("]]></trade_type>"); bf.append("</xml>"); return bf.toString(); } return ""; } /** * 構(gòu)造xml參數(shù) * @param xml * @return */ public static String xmlH5Info(Unifiedorder unifiedorder){ if(unifiedorder!=null){ StringBuffer bf = new StringBuffer(); bf.append("<xml>"); bf.append("<appid><![CDATA["); bf.append(unifiedorder.getAppid()); bf.append("]]></appid>"); bf.append("<mch_id><![CDATA["); bf.append(unifiedorder.getMch_id()); bf.append("]]></mch_id>"); bf.append("<nonce_str><![CDATA["); bf.append(unifiedorder.getNonce_str()); bf.append("]]></nonce_str>"); bf.append("<sign><![CDATA["); bf.append(unifiedorder.getSign()); bf.append("]]></sign>"); bf.append("<body><![CDATA["); bf.append(unifiedorder.getBody()); bf.append("]]></body>"); bf.append("<attach><![CDATA["); bf.append(unifiedorder.getAttach()); bf.append("]]></attach>"); bf.append("<out_trade_no><![CDATA["); bf.append(unifiedorder.getOut_trade_no()); bf.append("]]></out_trade_no>"); bf.append("<total_fee><![CDATA["); bf.append(unifiedorder.getTotal_fee()); bf.append("]]></total_fee>"); bf.append("<spbill_create_ip><![CDATA["); bf.append(unifiedorder.getSpbill_create_ip()); bf.append("]]></spbill_create_ip>"); bf.append("<notify_url><![CDATA["); bf.append(unifiedorder.getNotify_url()); bf.append("]]></notify_url>"); bf.append("<trade_type><![CDATA["); bf.append(unifiedorder.getTrade_type()); bf.append("]]></trade_type>"); bf.append("<scene_info><![CDATA["); bf.append(unifiedorder.getScene_info()); bf.append("]]></scene_info>"); bf.append("</xml>"); return bf.toString(); } return ""; } /** * 構(gòu)造退款xml參數(shù) * @param xml * @return */ public static String xmlTransfer(Transfers transfers){ if(transfers!=null){ StringBuffer bf = new StringBuffer(); bf.append("<xml>"); bf.append("<mch_appid><![CDATA["); bf.append(transfers.getMch_appid()); bf.append("]]></mch_appid>"); bf.append("<mchid><![CDATA["); bf.append(transfers.getMchid()); bf.append("]]></mchid>"); bf.append("<nonce_str><![CDATA["); bf.append(transfers.getNonce_str()); bf.append("]]></nonce_str>"); bf.append("<sign><![CDATA["); bf.append(transfers.getSign()); bf.append("]]></sign>"); bf.append("<partner_trade_no><![CDATA["); bf.append(transfers.getPartner_trade_no()); bf.append("]]></partner_trade_no>"); bf.append("<openid><![CDATA["); bf.append(transfers.getOpenid()); bf.append("]]></openid>"); bf.append("<check_name><![CDATA["); bf.append(transfers.getCheck_name()); bf.append("]]></check_name>"); bf.append("<amount><![CDATA["); bf.append(transfers.getAmount()); bf.append("]]></amount>"); bf.append("<desc><![CDATA["); bf.append(transfers.getDesc()); bf.append("]]></desc>"); bf.append("<spbill_create_ip><![CDATA["); bf.append(transfers.getSpbill_create_ip()); bf.append("]]></spbill_create_ip>"); bf.append("</xml>"); return bf.toString(); } return ""; } /** * post請求并得到返回結(jié)果 * @param requestUrl * @param requestMethod * @param output * @return */ public static String httpsRequest(String requestUrl, String requestMethod, String output) { try{ URL url = new URL(requestUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod(requestMethod); if (null != output) { OutputStream outputStream = connection.getOutputStream(); outputStream.write(output.getBytes("UTF-8")); outputStream.close(); } // 從輸入流讀取返回內(nèi)容 InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; connection.disconnect(); return buffer.toString(); }catch(Exception ex){ ex.printStackTrace(); } return ""; } }
加載證書
package com.wellness.platfront.common.weixinutil; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClients; public class HttpUtil { /** * 發(fā)送post請求 * * @param url * 請求地址 * @param outputEntity * 發(fā)送內(nèi)容 * @param isLoadCert * 是否加載證書 */ public static CloseableHttpResponse Post(String url, String outputEntity, boolean isLoadCert) throws Exception { HttpPost httpPost = new HttpPost(url); // 得指明使用UTF-8編碼,否則到API服務(wù)器XML的中文不能被成功識別 httpPost.addHeader("Content-Type", "text/xml"); httpPost.setEntity(new StringEntity(outputEntity, "UTF-8")); if (isLoadCert) { // 加載含有證書的http請求 return HttpClients.custom().setSSLSocketFactory(CertUtil.initCert()).build().execute(httpPost); } else { return HttpClients.custom().build().execute(httpPost); } } }
加載證書的類
package com.wellness.platfront.common.weixinutil; import java.io.File; import java.io.FileInputStream; import java.security.KeyStore; import javax.net.ssl.SSLContext; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.ssl.SSLContexts; /** * 加載證書的類 * @author * @since 2017/08/16 */ @SuppressWarnings("deprecation") public class CertUtil { private static WeixinConfigUtils config = new WeixinConfigUtils(); /** * 加載證書 */ public static SSLConnectionSocketFactory initCert() throws Exception { FileInputStream instream = null; KeyStore keyStore = KeyStore.getInstance("PKCS12"); instream = new FileInputStream(new File(weixinConstant.PATH)); keyStore.load(instream, config.mch_id.toCharArray()); if (null != instream) { instream.close(); } SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore,config.mch_id.toCharArray()).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); return sslsf; } }
解密微信退款中返回相關(guān)信息的字符串
package com.wellness.platfront.common.weixinutil; import java.security.Security; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.springframework.stereotype.Service; import com.wellness.platfront.common.wechat.utils.MD5; /** * 解密微信退款中返回相關(guān)信息的字符串 * @author * @since 2017/09/05 */ @Service public class AESDecodeUtil { public static final String ALGORITHM = "AES/ECB/PKCS7Padding"; public String decode(String reqInfo) throws Exception{ try { byte[] decodeBase64 = Base64.decodeBase64(reqInfo); String md5Key = MD5.MD5Encode(weixinConstant.KEY); Security.addProvider(new BouncyCastleProvider()); Cipher cipher = Cipher.getInstance(ALGORITHM); SecretKey keySpec = new SecretKeySpec(md5Key.getBytes(), "AES"); //生成加密解密需要的Key cipher.init(Cipher.DECRYPT_MODE, keySpec); byte[] decoded = cipher.doFinal(decodeBase64); String result = new String(decoded, "UTF-8"); return result; } catch (Exception e) { e.printStackTrace(); } return null; } }
微信解析xml:帶有CDATA格式的
package com.wellness.platfront.common.weixinutil; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringReader; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.xml.sax.InputSource; import com.wellness.platfront.entity.weixin.UnifiedorderResult; import com.wellness.platfront.entity.weixin.WXPayResult; /** * 微信解析xml:帶有CDATA格式的 * @author * @date 2017/08/10 */ public class JdomParseXmlUtils { /** * 1、統(tǒng)一下單獲取微信返回 * 解析的時候自動去掉CDMA * @param xml */ @SuppressWarnings("unchecked") public static UnifiedorderResult getUnifiedorderResult(String xml){ UnifiedorderResult unifieorderResult = new UnifiedorderResult(); try { StringReader read = new StringReader(xml); // 創(chuàng)建新的輸入源SAX 解析器將使用 InputSource 對象來確定如何讀取 XML 輸入 InputSource source = new InputSource(read); // 創(chuàng)建一個新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通過輸入源構(gòu)造一個Document Document doc; doc = (Document) sb.build(source); Element root = doc.getRootElement();// 指向根節(jié)點 List<Element> list = root.getChildren(); if(list!=null&&list.size()>0){ for (Element element : list) { System.out.println("key是:"+element.getName()+",值是:"+element.getText()); if("return_code".equals(element.getName())){ unifieorderResult.setReturn_code(element.getText()); } if("return_msg".equals(element.getName())){ unifieorderResult.setReturn_msg(element.getText()); } if("appid".equals(element.getName())){ unifieorderResult.setAppid(element.getText()); } if("mch_id".equals(element.getName())){ unifieorderResult.setMch_id(element.getText()); } if("nonce_str".equals(element.getName())){ unifieorderResult.setNonce_str(element.getText()); } if("sign".equals(element.getName())){ unifieorderResult.setSign(element.getText()); } if("result_code".equals(element.getName())){ unifieorderResult.setResult_code(element.getText()); } if("prepay_id".equals(element.getName())){ unifieorderResult.setPrepay_id(element.getText()); } if("trade_type".equals(element.getName())){ unifieorderResult.setTrade_type(element.getText()); } } } } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }catch (Exception e) { e.printStackTrace(); } return unifieorderResult; } /** * 獲取微信回調(diào)的參數(shù)并將xml解析成map * 解析的時候自動去掉CDMA * @param xml */ @SuppressWarnings("unchecked") public static Map<String, String> getWeixinResult(HttpServletRequest request,HttpServletResponse response){ Map<String, String> resultMap = new HashMap<>(); try { PrintWriter writer = response.getWriter(); InputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } outSteam.close(); inStream.close(); String result = new String(outSteam.toByteArray(), "utf-8"); StringReader read = new StringReader(result); InputSource source = new InputSource(read); // 創(chuàng)建一個新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通過輸入源構(gòu)造一個Document Document doc; doc = (Document) sb.build(source); Element root = doc.getRootElement();// 指向根節(jié)點 List<Element> list = root.getChildren(); if(list!=null&&list.size()>0){ for (Element element : list) { resultMap.put(element.getName(), element.getText()); } } return resultMap; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 2、微信回調(diào)后參數(shù)解析 * 解析的時候自動去掉CDMA * @param xml */ @SuppressWarnings("unchecked") public static WXPayResult getWXPayResult(String xml){ WXPayResult wXPayResult = new WXPayResult(); try { StringReader read = new StringReader(xml); // 創(chuàng)建新的輸入源SAX 解析器將使用 InputSource 對象來確定如何讀取 XML 輸入 InputSource source = new InputSource(read); // 創(chuàng)建一個新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通過輸入源構(gòu)造一個Document Document doc; doc = (Document) sb.build(source); Element root = doc.getRootElement();// 指向根節(jié)點 List<Element> list = root.getChildren(); if(list!=null&&list.size()>0){ for (Element element : list) { if("return_code".equals(element.getName())){ wXPayResult.setReturn_code(element.getText()); } if("return_msg".equals(element.getName())){ wXPayResult.setReturn_msg(element.getText()); } if("appid".equals(element.getName())){ wXPayResult.setAppid(element.getText()); } if("mch_id".equals(element.getName())){ wXPayResult.setMch_id(element.getText()); } if("nonce_str".equals(element.getName())){ wXPayResult.setNonce_str(element.getText()); } if("sign".equals(element.getName())){ wXPayResult.setSign(element.getText()); } if("result_code".equals(element.getName())){ wXPayResult.setResult_code(element.getText()); } if("return_msg".equals(element.getName())){ wXPayResult.setReturn_msg(element.getText()); } if("return_msg".equals(element.getName())){ wXPayResult.setReturn_msg(element.getText()); } if("return_msg".equals(element.getName())){ wXPayResult.setReturn_msg(element.getText()); } if("return_msg".equals(element.getName())){ wXPayResult.setReturn_msg(element.getText()); } if("return_msg".equals(element.getName())){ wXPayResult.setReturn_msg(element.getText()); } if("return_msg".equals(element.getName())){ wXPayResult.setReturn_msg(element.getText()); } if("return_msg".equals(element.getName())){ wXPayResult.setReturn_msg(element.getText()); } if("return_msg".equals(element.getName())){ wXPayResult.setReturn_msg(element.getText()); } } } } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }catch (Exception e) { e.printStackTrace(); } return wXPayResult; } }
package com.wellness.platfront.common.weixinutil; /** * dom解析 * @author * @date 2017/08/10 */ import java.io.IOException; import java.io.StringReader; import java.util.Iterator; import java.util.List; import org.apache.commons.lang.StringUtils; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.xml.sax.InputSource; public class ParseXMLUtils { /** * 1、DOM解析 */ @SuppressWarnings("rawtypes") public static void beginXMLParse(String xml){ Document doc = null; try { doc = DocumentHelper.parseText(xml); // 將字符串轉(zhuǎn)為XML Element rootElt = doc.getRootElement(); // 獲取根節(jié)點smsReport System.out.println("根節(jié)點是:"+rootElt.getName()); Iterator iters = rootElt.elementIterator("sendResp"); // 獲取根節(jié)點下的子節(jié)點sms while (iters.hasNext()) { Element recordEle1 = (Element) iters.next(); Iterator iter = recordEle1.elementIterator("sms"); while (iter.hasNext()) { Element recordEle = (Element) iter.next(); String phone = recordEle.elementTextTrim("phone"); // 拿到sms節(jié)點下的子節(jié)點stat值 String smsID = recordEle.elementTextTrim("smsID"); // 拿到sms節(jié)點下的子節(jié)點stat值 System.out.println(phone+":"+smsID); } } } catch (DocumentException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 2、DOM4j解析XML(支持xpath) * 解析的時候自動去掉CDMA * @param xml */ public static void xpathParseXml(String xml){ try { StringReader read = new StringReader(xml); SAXReader saxReader = new SAXReader(); Document doc = saxReader.read(read); String xpath ="/xml/appid"; System.out.print(doc.selectSingleNode(xpath).getText()); } catch (DocumentException e) { e.printStackTrace(); } } /** * 3、JDOM解析XML * 解析的時候自動去掉CDMA * @param xml */ @SuppressWarnings("unchecked") public static void jdomParseXml(String xml){ try { StringReader read = new StringReader(xml); // 創(chuàng)建新的輸入源SAX 解析器將使用 InputSource 對象來確定如何讀取 XML 輸入 InputSource source = new InputSource(read); // 創(chuàng)建一個新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通過輸入源構(gòu)造一個Document org.jdom.Document doc; doc = (org.jdom.Document) sb.build(source); org.jdom.Element root = doc.getRootElement();// 指向根節(jié)點 List<org.jdom.Element> list = root.getChildren(); if(list!=null&&list.size()>0){ for (org.jdom.Element element : list) { /*try{ methodName = element.getName(); Method m = v.getClass().getMethod("set" + methodName, new Class[] { String.class }); if(parseInt(methodName)){ m.invoke(v, new Object[] { Integer.parseInt(element.getText()) }); }else{ m.invoke(v, new Object[] { element.getText() }); } }catch(Exception ex){ ex.printStackTrace(); }*/ } } } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }catch (Exception e) { e.printStackTrace(); } } public static boolean parseInt(String key){ if(!StringUtils.isEmpty(key)){ if(key.equals("total_fee")||key.equals("cash_fee")||key.equals("coupon_fee")||key.equals("coupon_count")||key.equals("coupon_fee_0")){ return true; } } return false; } }
微信支付相關(guān)的常量
package com.wellness.platfront.common.weixinutil; /** * 微信支付相關(guān)的常量 * @author yangfuren * @since 2017/08/16 */ public class weixinConstant { /** * 微信支付API秘鑰 */ public static final String KEY = "eAO9gDBK7NjqkYEvRFB82CspUTINiWnD"; /** * url */ public static final String URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; /** * 退款url */ public static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund"; /** * 證書地址 */ public static final String PATH =""; /** * 付款url */ public static final String WITHDRAW_URL="https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; }
微信退款狀態(tài)常量
package com.wellness.platfront.common.weixinutil; /** * 微信退款狀態(tài)常量 * @author * @since 2017/09/04 */ public class WeixinRefundStatusConstant { //退款申請成功 public static final String APPLY_SUCCESS="退款申請成功"; //退款申請失敗 public static final String APPLY_FAIL="退款申請失敗"; //退款成功 public static final String REFUND_SUCCESS="退款成功"; //退款異常 public static final String REFUND_FAIL="退款異常"; //退款關(guān)閉 public static final String REFUND_CLOSED="退款關(guān)閉"; }
微信退款訂單信息
package com.wellness.platfront.entity.weixin; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; /** * 微信退款訂單信息 * @author * */ @Entity @Table(name = "weixin_refund_order") public class RefundOrder implements Serializable{ private static final long serialVersionUID = 1L; //退款訂單id public Integer refundOrderId; //微信公眾賬號 public String appid; //微信商戶號 public String mch_id; //微信訂單號 public String transaction_id; //商戶訂單號 public String out_trade_no; //商戶退款單號 public String out_refund_no; //微信退款單號 public String refund_id; //退款金額 public Integer refund_fee; //訂單總價格 public Integer tatol_fee; //退款發(fā)起時間 public Date create_time; //退款狀態(tài) 申請退款中 申請退款失敗 退款成功 退款異常 退款關(guān)閉 public String refund_status; //退款退還賬戶 public String refund_rew_account; //退款完成時間 public Date end_time; //訂單退還方式 public String refundType; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "refund_order_id", nullable = false) public Integer getRefundOrderId() { return refundOrderId; } public void setRefundOrderId(Integer refundOrderId) { this.refundOrderId = refundOrderId; } @Column(name = "appid", nullable = false) public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } @Column(name = "mch_id", nullable = false) public String getMch_id() { return mch_id; } public void setMch_id(String mch_id) { this.mch_id = mch_id; } @Column(name = "transaction_id", nullable = false) public String getTransaction_id() { return transaction_id; } public void setTransaction_id(String transaction_id) { this.transaction_id = transaction_id; } @Column(name = "out_trade_no", nullable = false) public String getOut_trade_no() { return out_trade_no; } public void setOut_trade_no(String out_trade_no) { this.out_trade_no = out_trade_no; } @Column(name = "out_refund_no", nullable = false) public String getOut_refund_no() { return out_refund_no; } public void setOut_refund_no(String out_refund_no) { this.out_refund_no = out_refund_no; } @Column(name = "refund_id", nullable = false) public String getRefund_id() { return refund_id; } public void setRefund_id(String refund_id) { this.refund_id = refund_id; } @Column(name = "refund_fee", nullable = false) public Integer getRefund_fee() { return refund_fee; } public void setRefund_fee(Integer refund_fee) { this.refund_fee = refund_fee; } @Column(name = "tatol_fee", nullable = false) public Integer getTatol_fee() { return tatol_fee; } public void setTatol_fee(Integer tatol_fee) { this.tatol_fee = tatol_fee; } @Column(name = "create_time", nullable = false) public Date getCreate_time() { return create_time; } public void setCreate_time(Date create_time) { this.create_time = create_time; } @Column(name = "refund_status", nullable = false) public String getRefund_status() { return refund_status; } public void setRefund_status(String refund_status) { this.refund_status = refund_status; } @Column(name = "refund_rew_account") public String getRefund_rew_account() { return refund_rew_account; } public void setRefund_rew_account(String refund_rew_account) { this.refund_rew_account = refund_rew_account; } @Column(name = "end_time") public Date getEnd_time() { return end_time; } public void setEnd_time(Date end_time) { this.end_time = end_time; } @Column(name = "refund_type") public String getRefundType() { return refundType; } public void setRefundType(String refundType) { this.refundType = refundType; } }
這個實體類中參數(shù)和微信官方需要的參數(shù)一致
package com.wellness.platfront.entity.weixin; import java.io.Serializable; import com.wellness.platfront.entity.member.Member; /** * 統(tǒng)一下單提交為微信的參數(shù) * @author * @date 2017年08月11日 */ public class Unifiedorder implements Serializable{ private static final long serialVersionUID = 1L; //微信支付表id private Integer weixinId; //微信分配的公眾賬號ID(企業(yè)號corpid即為此appId) private String appid; //商戶id private String mch_id; //終端設(shè)備號(門店號或收銀設(shè)備ID),注意:PC網(wǎng)頁或公眾號內(nèi)支付請傳"WEB" private String device_info; //隨機字符串:數(shù)字+大寫字母的組合,32位 private String nonce_str; //簽名 private String sign; //商品或支付單簡要描述 private String body; //商品名稱明細列表 private String detail; //附加參數(shù)(例如:用于區(qū)別本商戶不同的分店) private String attach; //商戶系統(tǒng)內(nèi)部的訂單號 private String out_trade_no; //貨幣類型:符合ISO 4217標準的三位字母代碼,默認人民幣:CNY private String fee_type; //總金額 private int total_fee; //APP和網(wǎng)頁支付提交[用戶端ip],Native支付填調(diào)用微信支付API的機器IP。 private String spbill_create_ip; //訂單生成時間,格式為yyyyMMddHHmmss, private String time_start; //訂單失效時間,格式為yyyyMMddHHmmss,最短失效時間間隔必須大于5分鐘[支付寶是30分鐘,同樣30分鐘] private String time_expire; //商品標記,代金券或立減優(yōu)惠功能的參數(shù) private String goods_tag; //接收微信支付異步通知回調(diào)地址 private String notify_url; //交易類型:JSAPI,NATIVE,APP h6為 MWEB private String trade_type; //trade_type=NATIVE,此參數(shù)必傳。此id為二維碼中包含的商品ID,商戶自行定義。 private String product_id; //no_credit--指定不能使用信用卡支付 private String limit_pay; //trade_type=JSAPI,此參數(shù)必傳,用戶在商戶appid下的唯一標識 private String openid; //商戶內(nèi)部自己的退款單號 private String out_refund_no; //退款總金額單位為分 private int refund_fee; //操作員的id默認為mch_id private String op_user_id; //微信官方提供的訂單號 private String prepayid; //記錄所對應(yīng)的member private Member member; //返回給微信的狀態(tài)碼(用于支付回調(diào)時) public String return_code; //微信h6支付時候的場景信息官方的信息模板 {"h6_info"://h6支付固定傳"h6_info" //{"type":"",//場景類型 "wap_url":"",//WAP網(wǎng)站URL地址"wap_name": ""http://WAP 網(wǎng)站名}} public String scene_info; public String getScene_info() { return scene_info; } public void setScene_info(String scene_info) { this.scene_info = scene_info; } public String getReturn_code() { return return_code; } public void setReturn_code(String return_code) { this.return_code = return_code; } public String getAppid() { return appid; } public String getMch_id() { return mch_id; } public String getDevice_info() { return device_info; } public String getNonce_str() { return nonce_str; } public String getSign() { return sign; } public String getBody() { return body; } public String getDetail() { return detail; } public String getAttach() { return attach; } public String getOut_trade_no() { return out_trade_no; } public String getFee_type() { return fee_type; } public int getTotal_fee() { return total_fee; } public String getSpbill_create_ip() { return spbill_create_ip; } public String getTime_start() { return time_start; } public String getTime_expire() { return time_expire; } public String getGoods_tag() { return goods_tag; } public String getNotify_url() { return notify_url; } public String getTrade_type() { return trade_type; } public String getProduct_id() { return product_id; } public String getLimit_pay() { return limit_pay; } public String getOpenid() { return openid; } public void setAppid(String appid) { this.appid = appid; } public void setMch_id(String mch_id) { this.mch_id = mch_id; } public void setDevice_info(String device_info) { this.device_info = device_info; } public void setNonce_str(String nonce_str) { this.nonce_str = nonce_str; } public void setSign(String sign) { this.sign = sign; } public void setBody(String body) { this.body = body; } public void setDetail(String detail) { this.detail = detail; } public void setAttach(String attach) { this.attach = attach; } public void setOut_trade_no(String out_trade_no) { this.out_trade_no = out_trade_no; } public void setFee_type(String fee_type) { this.fee_type = fee_type; } public void setTotal_fee(int total_fee) { this.total_fee = total_fee; } public void setSpbill_create_ip(String spbill_create_ip) { this.spbill_create_ip = spbill_create_ip; } public void setTime_start(String time_start) { this.time_start = time_start; } public void setTime_expire(String time_expire) { this.time_expire = time_expire; } public void setGoods_tag(String goods_tag) { this.goods_tag = goods_tag; } public void setNotify_url(String notify_url) { this.notify_url = notify_url; } public void setTrade_type(String trade_type) { this.trade_type = trade_type; } public void setProduct_id(String product_id) { this.product_id = product_id; } public void setLimit_pay(String limit_pay) { this.limit_pay = limit_pay; } public void setOpenid(String openid) { this.openid = openid; } public String getOut_refund_no() { return out_refund_no; } public void setOut_refund_no(String out_refund_no) { this.out_refund_no = out_refund_no; } public int getRefund_fee() { return refund_fee; } public void setRefund_fee(int refund_fee) { this.refund_fee = refund_fee; } public Integer getWeixinId() { return weixinId; } public void setWeixinId(Integer weixinId) { this.weixinId = weixinId; } public Member getMember() { return member; } public void setMember(Member member) { this.member = member; } public String getPrepayid() { return prepayid; } public void setPrepayid(String prepayid) { this.prepayid = prepayid; } public String getOp_user_id() { return op_user_id; } public void setOp_user_id(String op_user_id) { this.op_user_id = op_user_id; } }
統(tǒng)一下單微信返回的參數(shù)組(xml)
package com.wellness.platfront.entity.weixin; /** * 統(tǒng)一下單微信返回的參數(shù)組(xml) * @author * @date 2017年08月11日 */ public class UnifiedorderResult { private String appid;//appid private String mch_id;//商家id private String device_info;//設(shè)備號 private String nonce_str;//隨機字符串 private String sign;//簽名 private String result_code;//錯誤碼 private String err_code;//錯誤代碼 private String err_code_des;//錯誤返回的信息描述 private String trade_type;//調(diào)用接口提交的交易類型,取值如下:JSAPI,NATIVE,APP private String prepay_id;//微信生成的預(yù)支付回話標識,用于后續(xù)接口調(diào)用中使用,該值有效期為2小時 private String code_url;//trade_type為NATIVE是有返回,可將該參數(shù)值生成二維碼展示出來進行掃碼支付 private String return_code;//返回狀態(tài)碼SUCCESS/FAIL此字段是通信標識,非交易標識,交易是否成功需要查看result_code來判斷 private String return_msg;//返回信息 public String getAppid() { return appid; } public String getMch_id() { return mch_id; } public String getDevice_info() { return device_info; } public String getNonce_str() { return nonce_str; } public String getSign() { return sign; } public String getResult_code() { return result_code; } public String getErr_code() { return err_code; } public String getErr_code_des() { return err_code_des; } public String getTrade_type() { return trade_type; } public String getPrepay_id() { return prepay_id; } public String getCode_url() { return code_url; } public void setAppid(String appid) { this.appid = appid; } public void setMch_id(String mch_id) { this.mch_id = mch_id; } public void setDevice_info(String device_info) { this.device_info = device_info; } public void setNonce_str(String nonce_str) { this.nonce_str = nonce_str; } public void setSign(String sign) { this.sign = sign; } public void setResult_code(String result_code) { this.result_code = result_code; } public void setErr_code(String err_code) { this.err_code = err_code; } public void setErr_code_des(String err_code_des) { this.err_code_des = err_code_des; } public void setTrade_type(String trade_type) { this.trade_type = trade_type; } public void setPrepay_id(String prepay_id) { this.prepay_id = prepay_id; } public void setCode_url(String code_url) { this.code_url = code_url; } public String getReturn_code() { return return_code; } public String getReturn_msg() { return return_msg; } public void setReturn_code(String return_code) { this.return_code = return_code; } public void setReturn_msg(String return_msg) { this.return_msg = return_msg; } }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
新聞名稱:java實現(xiàn)微信退款功能
轉(zhuǎn)載源于:http://www.rwnh.cn/article26/pgsecg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、網(wǎng)站維護、靜態(tài)網(wǎng)站、網(wǎng)站導(dǎo)航、營銷型網(wǎng)站建設(shè)、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)