app集成微信支付服务器端开发(java)
2015-10-26 14:38
621 查看
一、微信支付太坑爹,废话不说了,下面是我的服务端微信支付开发过程和代码记录
二、首先去微信申请账户,这里有两个平台
1、微信公众平台
2、微信开放平台(https://open.weixin.qq.com)这里选择第二个
三、账户开通、开发者认证之后就可以进行微信支付开发了
1、微信统一下单接口调用获取预支付id
/** * 获取微信支付所需信息(统一下单接口调用) * @param backURL * @param mDealerOrderEntity * @param mCarInfoEntity * @return * @throws JSONException * @throws IOException * @throws JDOMException */ public Map<String, String> getWechatOrderInfo(String notifyUrl, MDealerOrderEntity mDealerOrderEntity, String body, HttpServletRequest request, HttpServletResponse response) throws Exception { Map<String, String> resultMap = new HashMap<String, String>(); //生成payPreId Map<String, String> payPreIdMap = WechatUtil.getPayPreId(mDealerOrderEntity.getGoodorderno(), body, notifyUrl, request.getRemoteAddr(), String.valueOf((int)(mDealerOrderEntity.getMoney()*100))); String prePayId = payPreIdMap.get("prepay_id"); if(StringUtils.isNotEmpty(prePayId)) { //生成调用微信APP参数 resultMap = WechatUtil.genPayReq(prePayId); } return resultMap; }
此方法返回的数据如下
{ "appid": "123132131", "noncestr": "416e5cf0acb7e553a880b7647903da6e", "packageValue ": "Sign=WXPay", "partnerid ": "1276000000", "prepayid ": "wx2015101611341514a3cbbbf90572184370", "timestamp ": "1444966497", "sign": "1DD72B07607B0B41D2827954150D89E9" }
2、服务器端接受微信支付结果通知
/** * 处理微信支付通知 * @param request * @return * @throws Exception */ public String saveWechatNotify(HttpServletRequest request, HttpServletResponse response) throws Exception { Map<String, String> noticeMap = XMLUtil.parseXml(request); String transactionId = noticeMap.get("transaction_id"); MWechatInfoEntity wechatInfoEntity = this.findEntityByProperty(MWechatInfoEntity.class, "transactionId", transactionId); //如果wechatInfoEntity存在,说明请求已经处理过,直接返回 if(wechatInfoEntity != null) { return "SUCCESS"; } String sign = noticeMap.get("sign"); noticeMap.remove("sign"); // 验签通过 if (WechatUtil.getSignVeryfy(noticeMap, sign)) { // 通信成功此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断 if ("SUCCESS".equals(noticeMap.get("return_code"))) { // 交易成功 if ("SUCCESS".equals(noticeMap.get("result_code"))) { // 商户订单号 String goodorderno = noticeMap.get("out_trade_no"); MDealerOrderEntity mDealerOrderEntity = this.findEntityByProperty(MDealerOrderEntity.class, "goodorderno", goodorderno); MCarInfoEntity mCarInfoEntity = this.get(MCarInfoEntity.class, mDealerOrderEntity.getCarid()); // 订单更新时间 mDealerOrderEntity.setUpdatetime(new Date()); // ------------------------------ // 处理业务开始 // ------------------------------ // 这里写自己业务相关 // ------------------------------ // 处理业务完毕 // ------------------------------ noticeMap.put("sign", sign); this.common99Service.saveWechatInfo(noticeMap, mDealerOrderEntity.getId()); } else { // 错误时,返回结果未签名,记录retcode、retmsg看失败详情。 System.out.println("查询验证签名失败或业务错误"); System.out.println("retcode:" + noticeMap.get("retcode") + " retmsg:" + noticeMap.get("retmsg")); } return "SUCCESS"; } else { System.out.println("后台调用通信失败"); } return "SUCCESS"; } else { System.out.println("通知签名验证失败"); } return null; }
3、上面代码用到的工具方法都在WechatUtil.java工具类中
package com.jim.iweb.haocheok.tenpay.util; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import org.apache.commons.httpclient.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.jdom2.JDOMException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class WechatUtil { private static Logger logger = LoggerFactory.getLogger(WechatUtil.class); public static final String TAG = "Wechat.Util"; private static final int timeout = 5000; public static byte[] httpPost(String url, String entity) throws URISyntaxException, IOException { if (url == null || url.length() == 0) { logger.info(TAG, "httpPost, url is null"); return null; } CloseableHttpClient httpClient = HttpClients.createDefault(); URIBuilder uriBuilder = new URIBuilder(url); HttpPost httpPost = new HttpPost(uriBuilder.build()); RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectionRequestTimeout(timeout).setConnectTimeout(timeout).build(); httpPost.setConfig(requestConfig); // 避免汉字乱码导致请求失败, httpPost.setEntity(new StringEntity(entity, "UTF-8")); CloseableHttpResponse resp = null; try { resp = httpClient.execute(httpPost); if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { logger.info(TAG, "httpGet fail, status code = " + resp.getStatusLine().getStatusCode()); return null; } return EntityUtils.toByteArray(resp.getEntity()); } catch (Exception e) { logger.info(TAG, "httpPost exception, e = " + e.getMessage()); e.printStackTrace(); return null; } finally { if (httpClient != null) { httpClient.close(); } if (resp != null) { resp.close(); } } } /** * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串 * * @param params * 需要排序并参与字符拼接的参数组 * @return 拼接后字符串 */ public static String createLinkString(Map<String, String> params) { List<String> keys = new ArrayList<String>(params.keySet()); Collections.sort(keys); String prestr = ""; for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); String value = params.get(key); if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符 prestr = prestr + key + "=" + value; } else { prestr = prestr + key + "=" + value + "&"; } } return prestr; } /** * 根据反馈回来的信息,生成签名结果 * * @param Params * 通知返回来的参数数组 * @param sign * 比对的签名结果 * @return 生成的签名结果 */ public static boolean getSignVeryfy(Map<String, String> Params, String sign) { // 过滤空值、sign与sign_type参数 // Map<String, String> sParaNew = AlipayCore.paraFilter(Params); // 获取待签名字符串 String preSignStr = createLinkString(Params); preSignStr += "&key=" + ConstantUtil.API_KEY; // 获得签名验证结果 String resultSign = MD5.getMessageDigest(preSignStr.getBytes()).toUpperCase(); // String resultSign = MD5Util.MD5Encode(preSignStr.toString(), "UTF-8").toLowerCase(); if (sign.equals(resultSign)) { return true; } else { return false; } } /** * 装配xml,生成请求prePayId所需参数 * * @param params * @return */ public static String toXml(List<NameValuePair> params) { StringBuilder sb = new StringBuilder(); sb.append("<xml>"); for (int i = 0; i < params.size(); i++) { sb.append("<" + params.get(i).getName() + ">"); sb.append(params.get(i).getValue()); sb.append("</" + params.get(i).getName() + ">"); } sb.append("</xml>"); return sb.toString(); } /** * 生成签名 */ public static String genPackageSign(List<NameValuePair> params) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < params.size(); i++) { sb.append(params.get(i).getName()); sb.append('='); sb.append(params.get(i).getValue()); sb.append('&'); } sb.append("key="); sb.append(ConstantUtil.API_KEY); String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase(); return packageSign; } /** * * @param goodOrderNo * @param body * @param noticeUrl * @param ip * @param totalFee * @return */ public static String genProductArgs(String goodOrderNo, String body, String noticeUrl, String ip, String totalFee) { StringBuffer xml = new StringBuffer(); try { String nonceStr = getNonceStr(); xml.append("</xml>"); List<NameValuePair> packageParams = new LinkedList<NameValuePair>(); packageParams.add(new BasicNameValuePair("appid", ConstantUtil.APP_ID)); packageParams.add(new BasicNameValuePair("body", body)); packageParams.add(new BasicNameValuePair("mch_id", ConstantUtil.MCH_ID)); packageParams.add(new BasicNameValuePair("nonce_str", nonceStr)); packageParams.add(new BasicNameValuePair("notify_url", noticeUrl)); packageParams.add(new BasicNameValuePair("out_trade_no", goodOrderNo)); packageParams.add(new BasicNameValuePair("spbill_create_ip", ip)); packageParams.add(new BasicNameValuePair("total_fee", totalFee)); packageParams.add(new BasicNameValuePair("trade_type", "APP")); String sign = genPackageSign(packageParams); packageParams.add(new BasicNameValuePair("sign", sign)); String xmlstring = toXml(packageParams); return xmlstring; } catch (Exception e) { logger.info("genProductArgs fail, ex = " + e.getMessage()); return null; } } /** * 生成app支付签名 * * @param params * @return */ public static String genAppSign(List<NameValuePair> params) { StringBuilder sb = ne b84d w StringBuilder(); for (int i = 0; i < params.size(); i++) { sb.append(params.get(i).getName()); sb.append('='); sb.append(params.get(i).getValue()); sb.append('&'); } sb.append("key="); sb.append(ConstantUtil.API_KEY); String appSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase(); logger.info("orion", appSign); return appSign; } /** * 生成调用微信app支付所需参数 * * @param prepayId * @return */ public static Map<String, String> genPayReq(String prepayId) { Map<String, String> resultMap = new HashMap<String, String>(); String timeStamp = getTimeStamp(); String nonceStr = getNonceStr(); List<NameValuePair> signParams = new LinkedList<NameValuePair>(); signParams.add(new BasicNameValuePair("appid", ConstantUtil.APP_ID)); signParams.add(new BasicNameValuePair("noncestr", nonceStr)); signParams.add(new BasicNameValuePair("package", "Sign=WXPay")); signParams.add(new BasicNameValuePair("partnerid", ConstantUtil.MCH_ID)); signParams.add(new BasicNameValuePair("prepayid", prepayId)); signParams.add(new BasicNameValuePair("timestamp", timeStamp)); String sign = genAppSign(signParams); resultMap.put("appid", ConstantUtil.APP_ID); resultMap.put("noncestr", nonceStr); resultMap.put("packageValue", "Sign=WXPay"); resultMap.put("partnerid", ConstantUtil.MCH_ID); resultMap.put("prepayid", prepayId); resultMap.put("timestamp", timeStamp); resultMap.put("sign", sign); return resultMap; } /** * 微信支付生成预支付订单 * * @throws IOException * @throws JDOMException */ public static Map<String, String> getPayPreId(String goodOrderNo, String body, String noticeUrl, String ip, String totalFee) throws Exception { String paramsXml = genProductArgs(goodOrderNo, body, noticeUrl, ip, totalFee); logger.info("orion", paramsXml); byte[] buf = WechatUtil.httpPost(ConstantUtil.URL, paramsXml); String contentXml = new String(buf); Map<String, String> resultMap = XMLUtil.doXMLParse(contentXml); return resultMap; } public static String getNonceStr() { Random random = new Random(); return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes()); } public static String getTimeStamp() { return String.valueOf(System.currentTimeMillis() / 1000); } }
4、下面是用到的配置类
package com.jim.iweb.haocheok.tenpay.util; public class ConstantUtil { /** * 商家可以考虑读取配置文件 */ //初始化 public static String APP_ID = "wxsdfsdfsf5fdbc";//微信开发平台应用id public static String APP_SECRET = "aab95csdfsdfsffdcsdfsfs0df34";//应用对应的凭证 //商户号 public static String MCH_ID = "1233312201"; public static String PARTNER = "1233312201";//财付通商户号 public static String API_KEY = "KgjyjirmjajdfjsdjfsjffVpT6RMbrB"; public static String PARTNER_KEY = "KgjyjirmjajdfjsdjfsjffVpT6RMbrB";//商户号对应的密钥 public static String URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//获取预支付id的接口url }
5、xml 解析工具类
package com.jim.iweb.haocheok.tenpay.util; public class ConstantUtil { /** * 商家可以考虑读取配置文件 */ //初始化 public static String APP_ID = "wxsdfsdfsf5fdbc";//微信开发平台应用id public static String APP_SECRET = "aab95csdfsdfsffdcsdfsfs0df34";//应用对应的凭证 //商户号 public static String MCH_ID = "1233312201"; public static String PARTNER = "1233312201";//财付通商户号 public static String API_KEY = "KgjyjirmjajdfjsdjfsjffVpT6RMbrB"; public static String PARTNER_KEY = "KgjyjirmjajdfjsdjfsjffVpT6RMbrB";//商户号对应的密钥 public static String URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//获取预支付id的接口url }
5、xml 解析工具类
package com.jim.iweb.haocheok.tenpay.util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.dom4j.io.SAXReader; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.input.SAXBuilder; /** * xml工具类 * * @author miklchen * */ public class XMLUtil { /** * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 * * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map doXMLParse(String strxml) throws JDOMException, IOException { strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); if (null == strxml || "".equals(strxml)) { return null; } Map m = new HashMap(); InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(in); Element root = doc.getRootElement(); List list = root.getChildren(); Iterator it = list.iterator(); while (it.hasNext()) { Element e = (Element) it.next(); String k = e.getName(); String v = ""; List children = e.getChildren(); if (children.isEmpty()) { v = e.getTextNormalize(); } else { v = XMLUtil.getChildrenText(children); } m.put(k, v); } // 关闭流 in.close(); return m; } /** * 获取子结点的xml * * @param children * @return String */ public static String getChildrenText(List children) { StringBuffer sb = new StringBuffer(); if (!children.isEmpty()) { Iterator it = children.iterator(); while (it.hasNext()) { Element e = (Element) it.next(); String name = e.getName(); String value = e.getTextNormalize(); List list = e.getChildren(); sb.append("<" + name + ">"); if (!list.isEmpty()) { sb.append(XMLUtil.getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } /** * 将requestxml通知结果转出啊成map * @param request * @return * @throws Exception */ public static Map<String, String> parseXml(HttpServletRequest request) throws Exception { // 解析结果存储在HashMap Map<String, String> map = new HashMap<String, String>(); InputStream inputStream = request.getInputStream(); // 读取输入流 SAXReader reader = new SAXReader(); org.dom4j.Document document = reader.read(inputStream); // 得到xml根元素 org.dom4j.Element root = document.getRootElement(); // 得到根元素的所有子节点 List<org.dom4j.Element> elementList = root.elements(); // 遍历所有子节点 for (org.dom4j.Element e : elementList) map.put(e.getName(), e.getText()); // 释放资源 inputStream.close(); inputStream = null; return map; } }
?
相关文章推荐
- 社交巨头三国杀:微信、WhatsApp、Line到底有啥区别?
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- 小心服务器内存居高不下的元凶--WebAPI服务
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- 微信悄悄升级群聊功能:个人微信营销号的福音
- 运维入门
- PropertyChangeListener简单理解
- 个人信息安全报告发布:有 APP 每分钟调用位置权限 1468 次
- 插入排序