一、微信支付太坑爹,废话不说了,下面是我的服务端微信支付开发过程和代码记录
二、首先去微信申请账户,这里有两个平台
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 = 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 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;
- }
- }