微信支付 APP支付 Java 服务器端

本文介绍微信支付中APP支付的java服务端。
微信APP支付文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1
编写之前请先浏览文档中的业务流程、API列表中的统一下单和支付结果通知。
调用需要用到微信商户的APPID、商户号、API密钥。请参考微信支付文档申请和获取。
加入微信支付需要的jar.或者配置maven
需要加入

<dependency><groupId>org.jdom</groupId><artifactId>jdom</artifactId><version>1.0</version></dependency>

下面直接上代码。
一:商户后台收到用户支付单,调用微信支付统一下单接口。
服务器端提供接口,接收APP端传过来的订单id。服务器端根据id查询订单的信息。
注意:
1.微信的价格是需要乘以100的。也就是传过去1元,显示的是0.01元。
2.回调的地址必须是外网可以访问的。我的网是公司内网,所以用了花生壳弄了个映射。至于怎么使用,大家自行百度。

/*** 微信统一下单接口* @param request* @param response*/@ResponseBody@RequestMapping("/wxPrePay.html")public Map<String,Object> wxPrePay(HttpServletRequest request,HttpServletResponse response){Map<String, Object> resultMap = new HashMap<String, Object>();GoodsTrade goodsTrade = goodsTradeService.queryGoodsTradeById(request.getParameter("tradeId"));//获取订单,根据需要自己编写String price = goodsTrade.getPrice();int price100 = new BigDecimal(price).multiply(new BigDecimal(100)).intValue();if(price100<=0){resultMap.put("msg","付款金额错误")resultMap.put("code","500");return resultMap;}//设置回调地址-获取当前的地址拼接回调地址String url = request.getRequestURL().toString();String domain = url.substring(0, url.length()-13);//生产环境String notify_url= domain+"wxNotify.html";//测试环境//String notify_url = "http://1f504p5895.51mypc.cn/cia/app/wxNotify.html";SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();parameters.put("appid", ConfigUtil.APPID);  parameters.put("mch_id", ConfigUtil.MCH_ID);  parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());  parameters.put("body", "购买测试");  parameters.put("out_trade_no", goodsTrade.getTid()); //订单idparameters.put("fee_type", "CNY");  parameters.put("total_fee", String.valueOf(price100));  parameters.put("spbill_create_ip",CommonUtil.toIpAddr(request));parameters.put("notify_url", notify_url); parameters.put("trade_type", "APP");  //设置签名String sign = PayCommonUtil.createSign("UTF-8",parameters);parameters.put("sign", sign);//封装请求参数结束String requestXML = PayCommonUtil.getRequestXml(parameters);  //调用统一下单接口String result = PayCommonUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL, "POST", requestXML);System.out.println("\n"+result);try {/**统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay**/Map<String, String> map = XMLUtil.doXMLParse(result);SortedMap<Object, Object> parameterMap2 = new TreeMap<Object, Object>();  parameterMap2.put("appid", ConfigUtil.APPID);  parameterMap2.put("partnerid", ConfigUtil.MCH_ID);  parameterMap2.put("prepayid", map.get("prepay_id"));  parameterMap2.put("package", "Sign=WXPay");  parameterMap2.put("noncestr", PayCommonUtil.CreateNoncestr());  //本来生成的时间戳是13位,但是ios必须是10位,所以截取了一下parameterMap2.put("timestamp", Long.parseLong(String.valueOf(System.currentTimeMillis()).toString().substring(0,10)));  String sign2 = PayCommonUtil.createSign("UTF-8",parameterMap2);parameterMap2.put("sign", sign2);  resultMap.put("code","200");resultMap.put("msg",parameterMap2);} catch (JDOMException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return resultMap;}

二:商户后台接收支付通知
接下来是微信的回调。当用户支付后,微信服务器端会给我们的服务器端发送异步回调。
做该步骤值之前,请先详细参考文档https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7&index=3

上代码:

/*** 微信异步通知*/@RequestMapping("/wxNotify.html")public void wxNotify(HttpServletRequest request,HttpServletResponse response) throws IOException, JDOMException{//读取参数  InputStream inputStream ;  StringBuffer sb = new StringBuffer();  inputStream = request.getInputStream();  String s ;  BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));  while ((s = in.readLine()) != null){  sb.append(s);  }  in.close();  inputStream.close();  //解析xml成map  Map<String, String> m = new HashMap<String, String>();  m = XMLUtil.doXMLParse(sb.toString());  for(Object keyValue : m.keySet()){System.out.println(keyValue+"="+m.get(keyValue));}//过滤空 设置 TreeMap  SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();        Iterator it = m.keySet().iterator();  while (it.hasNext()) {  String parameter = (String) it.next();  String parameterValue = m.get(parameter);  String v = "";  if(null != parameterValue) {  v = parameterValue.trim();  }  packageParams.put(parameter, v);  }  //判断签名是否正确  String resXml = "";  if(PayCommonUtil.isTenpaySign("UTF-8", packageParams)) {if("SUCCESS".equals((String)packageParams.get("result_code"))){ // 这里是支付成功  //执行自己的业务逻辑  String mch_id = (String)packageParams.get("mch_id"); //商户号 String openid = (String)packageParams.get("openid");  //用户标识String out_trade_no = (String)packageParams.get("out_trade_no"); //商户订单号String total_fee = (String)packageParams.get("total_fee");  String transaction_id = (String)packageParams.get("transaction_id"); //微信支付订单号GoodsTrade gt = new GoodsTrade();gt.setTid(out_trade_no);//查询订单 根据订单号查询订单  GoodsTrade -订单实体类GoodsTrade trade = 订单查询;if(!ConfigUtil.MCH_ID.equals(mch_id)||trade==null||new BigDecimal(total_fee).compareTo(new BigDecimal(trade.getPrice()).multiply(new BigDecimal(100))) != 0){logger.info("支付失败,错误信息:" + "参数错误");  resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"  + "<return_msg><![CDATA[参数错误]]></return_msg>" + "</xml> ";  }else{if("no_pay".equals(trade.getPayStatus()) && "wait_buyer_pay".equals(trade.getStatus())){//支付的价格//订单状态的修改。根据实际业务逻辑执行                    resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"  + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";  }else{resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"  + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";  logger.info("订单已处理");  }}}else {  logger.info("支付失败,错误信息:" + packageParams.get("err_code"));  resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"  + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";  }  } else{  resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"  + "<return_msg><![CDATA[通知签名验证失败]]></return_msg>" + "</xml> "; logger.info("通知签名验证失败");  }   //------------------------------  //处理业务完毕  //------------------------------  BufferedOutputStream out = new BufferedOutputStream(  response.getOutputStream());  out.write(resXml.getBytes());  out.flush();  out.close();  }

接下来提供以上用到代码的工具类.

ConfigUtil.java

package com.cia.util.wxpay;public class ConfigUtil {/*** 服务号相关信息*/public final static String APPID = "***";//服务号的应用号public final static String MCH_ID = "***";//商户号public final static String API_KEY = "***";//API密钥public final static String SIGN_TYPE = "MD5";//签名加密方式public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
}

PayCommonUtil.java

package com.cia.util.wxpay;import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URL;import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;import com.alibaba.fastjson.JSONObject;public class PayCommonUtil {public static String CreateNoncestr(int length) {String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";String res = "";for (int i = 0; i < length; i++) {Random rd = new Random();res += chars.indexOf(rd.nextInt(chars.length() - 1));}return res;}public static String CreateNoncestr() {String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";String res = "";for (int i = 0; i < 16; i++) {Random rd = new Random();res += chars.charAt(rd.nextInt(chars.length() - 1));}return res;}/** * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 * @return boolean */  public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams) {  StringBuffer sb = new StringBuffer();  Set es = packageParams.entrySet();  Iterator it = es.iterator();  while(it.hasNext()) {  Map.Entry entry = (Map.Entry)it.next();  String k = (String)entry.getKey();  String v = (String)entry.getValue();  if(!"sign".equals(k) && null != v && !"".equals(v)) {  sb.append(k + "=" + v + "&");  }  }  sb.append("key=" + ConfigUtil.API_KEY);  //算出摘要  String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();  String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();  //System.out.println(tenpaySign + "    " + mysign);  return tenpaySign.equals(mysign);  }  /*** @Description:sign签名* @param characterEncoding 编码格式* @param parameters 请求参数* @return*/public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){StringBuffer sb = new StringBuffer();Set es = parameters.entrySet();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="+ConfigUtil.API_KEY);String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();return sign;}/*** @Description:将请求参数转换为xml格式的string* @param parameters  请求参数* @return*/public static String getRequestXml(SortedMap<Object,Object> parameters){StringBuffer sb = new StringBuffer();sb.append("<xml>");Set es = parameters.entrySet();Iterator it = es.iterator();while(it.hasNext()) {Map.Entry entry = (Map.Entry)it.next();String k = (String)entry.getKey();String v = (String)entry.getValue();if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)) {sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");}else {sb.append("<"+k+">"+v+"</"+k+">");}}sb.append("</xml>");return sb.toString();}/*** @Description:返回给微信的参数* @param return_code 返回编码* @param return_msg  返回信息* @return*/public static String setXML(String return_code, String return_msg) {return "<xml><return_code><![CDATA[" + return_code+ "]]></return_code><return_msg><![CDATA[" + return_msg+ "]]></return_msg></xml>";}/*** 发送https请求* @param requestUrl 请求地址* @param requestMethod 请求方式(GET、POST)* @param outputStr 提交的数据* @return 返回微信服务器响应的信息*/public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {try {// 创建SSLContext对象,并使用我们指定的信任管理器初始化TrustManager[] tm = { new MyX509TrustManager() };SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");sslContext.init(null, tm, new java.security.SecureRandom());// 从上述SSLContext对象中得到SSLSocketFactory对象SSLSocketFactory ssf = sslContext.getSocketFactory();URL url = new URL(requestUrl);HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();//conn.setSSLSocketFactory(ssf);conn.setDoOutput(true);conn.setDoInput(true);conn.setUseCaches(false);// 设置请求方式(GET/POST)conn.setRequestMethod(requestMethod);conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); // 当outputStr不为null时向输出流写数据if (null != outputStr) {OutputStream outputStream = conn.getOutputStream();// 注意编码格式outputStream.write(outputStr.getBytes("UTF-8"));outputStream.close();}// 从输入流读取返回内容InputStream inputStream = conn.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;conn.disconnect();return buffer.toString();} catch (ConnectException ce) {
//          log.error("连接超时:{}", ce);} catch (Exception e) {
//          log.error("https请求异常:{}", e);}return null;}/*** 发送https请求* * @param requestUrl 请求地址* @param requestMethod 请求方式(GET、POST)* @param outputStr 提交的数据* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)*/public static JSONObject httpsRequest(String requestUrl, String requestMethod) {JSONObject jsonObject = null;try {// 创建SSLContext对象,并使用我们指定的信任管理器初始化TrustManager[] tm = { new MyX509TrustManager() };SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");sslContext.init(null, tm, new java.security.SecureRandom());// 从上述SSLContext对象中得到SSLSocketFactory对象SSLSocketFactory ssf = sslContext.getSocketFactory();URL url = new URL(requestUrl);HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();//conn.setSSLSocketFactory(ssf);conn.setDoOutput(true);conn.setDoInput(true);conn.setUseCaches(false);conn.setConnectTimeout(3000);// 设置请求方式(GET/POST)conn.setRequestMethod(requestMethod);//conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); // 当outputStr不为null时向输出流写数据// 从输入流读取返回内容InputStream inputStream = conn.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;conn.disconnect();jsonObject = JSONObject.parseObject(buffer.toString());} catch (ConnectException ce) {
//                    log.error("连接超时:{}", ce);} catch (Exception e) {System.out.println(e);
//                    log.error("https请求异常:{}", e);}return jsonObject;
}public static String urlEncodeUTF8(String source){String result = source;try {result = java.net.URLEncoder.encode(source,"utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return result;}
}

XMLUtil .java

    package com.cia.util.wxpay;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 org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;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();}}

MD5Util.java

package com.cia.util.wxpay;
import java.security.MessageDigest;public class MD5Util {private static String byteArrayToHexString(byte b[]) {StringBuffer resultSb = new StringBuffer();for (int i = 0; i < b.length; i++)resultSb.append(byteToHexString(b[i]));return resultSb.toString();}private static String byteToHexString(byte b) {int n = b;if (n < 0)n += 256;int d1 = n / 16;int d2 = n % 16;return hexDigits[d1] + hexDigits[d2];}public static String MD5Encode(String origin, String charsetname) {String resultString = null;try {resultString = new String(origin);MessageDigest md = MessageDigest.getInstance("MD5");if (charsetname == null || "".equals(charsetname))resultString = byteArrayToHexString(md.digest(resultString.getBytes()));elseresultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));} catch (Exception exception) {}return resultString;}private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5","6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };}

MyX509TrustManager.java

package com.cia.util.wxpay;import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;/*** 信任管理器*/
public class MyX509TrustManager implements X509TrustManager {// 检查客户端证书public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 检查服务器端证书public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 返回受信任的X509证书数组public X509Certificate[] getAcceptedIssuers() {return null;}
}

以上统一下单及回调中的订单的查询,根据自己的实际情况提供service查询,就不写出来了。所以贴上来可能有错,把错误的地方改成自己的代码就行了。

微信支付 APP支付 Java 服务器端相关推荐

  1. 支付宝支付 App支付以及网页支付接入流程

    支付宝支付申请流程 该文档支付包含两部分:支付宝App支付以及支付宝网页支付(手机网站支付) 注:使用时根据需要去开通即可 前提: 1.注册支付宝开发者账户,注册流程参见<支付宝开发者帐号注册及 ...

  2. 微信手机app支付java

    java的uniapp版本的微信支付 准备工作 1.先准备企业账号,然后创建应用,如图所示 创建应用获取app支付 3.查看appid,AppSecret 2.根据官方文档生成公钥私钥 支付宝官方地址 ...

  3. JAVA支付宝和微信(APP支付,提现,退款)

    公共参数图表: 接口 需要参数 通知方式 支付宝APP支付 应用公钥,应用私钥 异步 支付宝APP提现 应用公钥,应用私钥,支付宝公钥 同步 支付宝APP退款 应用公钥,应用私钥,支付宝公钥 同步 微 ...

  4. 微信支付-App支付服务端详解

    微信App支付服务端详解 引言 主要实现app支付统一下单.异步通知.调起支付接口.支付订单查询.申请退款.查询退款功能:封装了https对发起退款的证书校验.签名.xml解析等. 支付流程 具体支付 ...

  5. 微信支付app支付3.0接口开发

    最近在做微信支付,因为前段时间做了微信的公众账号支付,我以为是一个东西,结果发现不是,我真是醉了,竟然是独立的两套东西. 整个微信支付,分为三大平台,公众平台(就是公众账号那个),开发平台(主要针对a ...

  6. php微信支付扫码源码下载,微信支付:扫码支付+APP支付

    微信支付:实现了扫码支付.app支付和回调辅助函数 按照微信支付官方SDK的PHP版本,实现了python版本 支付回调定义 @csrf_exempt def wechat_pay_callback( ...

  7. 关于微信的APP支付、公众号(服务号)支付,商户问题

    注意:截止2017-10-17日 微信支付背影说明: 1. 先开通APP支付(SDK方式),通过微信开放平台开通(300元)现在审核很严格.申请完成后通过邮件下达商户账号,商户平台绑定公司的对公银行账 ...

  8. 微信支付-APP支付

    1. 登陆商户平台 查看APPID , https://pay.weixin.qq.com 在产品中心->我的产品 中查看当前商户开的支付类型. 然后在appID授权里边查看APPID. 然后在 ...

  9. 微信支付APP支付完全攻略

    微信支付接入应用 最近项目中要接入微信支付,所以就去研究了下,发现坑还真不少啊.也就有了这篇博客. 接入前准备 在开始接入微信支付前你的去微信的开发平台上面注册你自己的应用.具体的链接是微信开发平台 ...

最新文章

  1. Digital Realty计划在德克萨斯州建设三层数据中心
  2. Storm的StreamID使用样例(版本1.0.2)
  3. php array in array,浅谈PHP array_search 和 in_array 函数效率问题
  4. Codeforces div2 #499 B. Planning The Expedition 大水题
  5. C++编程思想:文件 字符串 输入输出
  6. Qt OpenGL环境配置的问题
  7. 前端学习(2505):小游戏设计
  8. 动态获取textarea后面的p标签_HTML简单标签连起实现的小玩意:
  9. 优达学城深度学习之一——Anaconda
  10. pandas Dataframe删除缺失值
  11. linux 和 windows usb插拔侦测
  12. Anaconda——复制Conda的虚拟环境
  13. 【调度问题】基于遗传算法求解公交排班系统matlab源码
  14. 自考马克思主义原理基本概述【03709】必做选择题
  15. 2021年起重机司机(限桥式起重机)考试题库及起重机司机(限桥式起重机)考试试卷
  16. 五个方向告诉你如何运营好一个公众号!
  17. Matlab实现snn代码,SNN系列|神经元模型篇(3)SRM
  18. DRM系列(10)之drm_read
  19. Hi-C Data Browser:Hi-C数据浏览器
  20. 关于Visual Studio 2017安装时共享组件、工具和 SDK安装位置不能更改的问题

热门文章

  1. 数据分析师三个等级_数据分析课|这三个等级的数据分析师报考条件,一定是你需要的...
  2. wincc做皮带动画_WINCC中制作管道流体流动动画的一种方法
  3. python有vlookup的功能么_Python 3中的条件vlookup
  4. C语言——深度剖析数据在内存中的存储
  5. python抓取日本网站上iphone5的价格
  6. 任正非:不惑之年创立华为是生活所迫,CEO真不是人干的活
  7. 202个DIY及科技网站大集合
  8. 【数学】-1的根号二次方等于多少?
  9. c语言开发无人机自动驾驶仪,无人机自动驾驶仪.pdf
  10. 【新型密集采样网络:遥感超分】