APP调起微信支付,JAVA服务端统一下单
来源:互联网 发布:小满软件怎么样 编辑:程序博客网 时间:2024/05/16 04:21
原理概述:
微信支付分为公众号发起,PC网页扫码,APP用于APP端调起的支付
这篇文章主要讨论APP掉起支付,包括统一下单,微信接口回调,以及一些注意事项
微信官方文档:
https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
这里有详细的入参列表,调用地址,但是我表示,看完文档依旧写不出可以马上可用的接口
源码详述:
nonce_str参数:
生成随机数算法,用于统一下单的nonce_str参数
public static String getRandomStringByLength(int length) { String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < length; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); }
获取统一下单接口地址:
https://api.mch.weixin.qq.com/pay/unifiedorder
从微信颁发的参数:
应用ID:appid ; 商户号:mch_id ;微信支付key:用户自己设置的支付key(我的程序里叫wx_key)
以上三个参数,来自微信商户,登录商户账号,可以找到,在支付时直接把参数写进去就行
需要客户端必须传递给服务端的参数:
商户订单号:out_trade_no 这个参数是微信自己掉起支付生成的订单号,服务器需要这个参数去微信服务器下单
总金额:total_fee 这个参数如果客户端转化成分,那么服务端就不用转换了,总之最后要传分为单位的数值关于商品属性相关的比如商品详情detail ;商品描述body ;附加数据 attach ;订单优惠标记 :goods_tag,可以服务写死,也可以客户端传递过来
微信回调地址:
这个参数是微信在接收到支付前款后,主动调用你的应用服务器,用来通知支付成功的
这个地址是需要你自己来写一个controler,不要限制请求,不要携带参数,并且微信支付需要时80端口
一下为我写的一个回调的源码
/** * 微信支付回调函数 * @param request * @param response * @throws IOException */ @RequestMapping(value = "/callback") public void callBack(HttpServletRequest request,HttpServletResponse response) throws IOException { InputStream is = request.getInputStream(); HashMap<String, String> map = new HashMap<String, String>(); SAXReader reader = new SAXReader(); Document document = null; try { document = reader.read(is); } catch (DocumentException e1) { e1.printStackTrace(); } String out_trade_no = "";//订单ID String total_fee = ""; //订单金额 Element root = document.getRootElement(); List<Element> list = root.elements(); // 获取微信返回值信息 for (Element e : list) { map.put(e.getName().trim(), e.getText().trim()); if (e.getName().trim().equals("out_trade_no")) { out_trade_no = e.getText().trim(); } else if (e.getName().trim().equals("cash_fee")) { total_fee = e.getText().trim(); } } is.close(); // 克隆传入的信息并进行验签,建议一定要验证签名,防止返回值被篡改 HashMap<String, String> signMap = (HashMap<String, String>) map.clone(); signMap.remove("sign"); // 这里的wx_key 是用户自定义的支付key String key= PropertiesUtil.getValue("wechat.properties","wx_key"); String sign = SignatureUtils.signature(signMap,key); if (!sign.equals(map.get("sign"))) { return; } // 信息处理 String result_code = map.get("result_code"); try { if ("SUCCESS".equals(result_code)) { //由于微信后台会同时回调多次,所以需要做防止重复提交操作的判断 //此处放防止重复提交操作 } else if ("FAIL".equals(result_code)) { } } catch (Exception e) { e.printStackTrace(); return; } //这里是验证返回值没问题了,可以写具体的支付成功的逻辑 // 返回信息,防止微信重复发送报文 String result = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml>"; PrintWriter out = new PrintWriter(response.getOutputStream()); out.print(result); out.flush(); out.close(); }
验签源码:(用于统一下单,微信回调)
这个代码生成的值用于同一下单的 签名:sign 参数,也用于验证微信回调的返回值
提供一个微信工具,用来计算签名的,可以本地计算完和他比对,实际开发过程中会出现各种情况的签名错误,用这个你可以很快找到问题:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=20_1源码:
/** * 微信支付加密工具 */public static String signature(Map<String, String> map, String key) {Set<String> keySet = map.keySet();String[] str = new String[map.size()];StringBuilder tmp = new StringBuilder();// 进行字典排序str = keySet.toArray(str);Arrays.sort(str);for (int i = 0; i < str.length; i++) {String t = str[i] + "=" + map.get(str[i]) + "&";tmp.append(t);}if (StringUtils.isNotBlank(key)) {tmp.append("key=" + key);}String tosend = tmp.toString();MessageDigest md = null;byte[] bytes = null;try {md = MessageDigest.getInstance("MD5");bytes = md.digest(tosend.getBytes("utf-8"));} catch (Exception e) {e.printStackTrace();}String singe = byteToStr(bytes);return singe.toUpperCase();}
统一下单源码:
注意,统一下单并不是支付成功,他只是吧这个订单号在微信服务那里生成了预支付信息,真正的支付成功还要看微信回调
public static synchronized JSONObject createOrder(String detail, String desc, String openid, String ip, String goodSn, String orderSn, String amount, String type,String randomNum) { JSONObject result = new JSONObject(); double relAmount = 0;// 对应微信支付的真实数目 try { //微信接收的金额单位是分,如果客户端不转换,服务端要讲金额转换成分 relAmount = Double.parseDouble(amount) * 100; } catch (Exception e) { return result; } if (relAmount == 0) { //微信支付的支付金额必须为大于0的int类型,单位为分 return result; } if (!("JSAPI".equalsIgnoreCase(type) || "NATIVE".equalsIgnoreCase(type) || "APP".equalsIgnoreCase(type))) { return result; } // 获取系统配置信息 String wx_order = PropertiesUtil.getValue("wechat.properties", "wx_order");//获取统一下单接口地址 String mchappid = PropertiesUtil.getValue("wechat.properties", "mchappid");// 商户appid String mchid = PropertiesUtil.getValue("wechat.properties", "mchid");// 商户ID String wx_callback = PropertiesUtil.getValue("wechat.properties", "wx_callback");// 获取微信支付回调接口 String wx_key = PropertiesUtil.getValue("wechat.properties", "wx_key");//微信商户后台设置的key String app_mchid = PropertiesUtil.getValue("wechat.properties", "app_mchid");//APP调起微信支付的商户ID String app_mchappid = PropertiesUtil.getValue("wechat.properties", "app_mchappid");//APP调起微信的APPID if (StringUtils.isBlank(wx_order) || StringUtils.isBlank(mchappid)|| StringUtils.isBlank(mchid) || StringUtils.isBlank(wx_callback)) { return result; } // 发送报文模板,其中部分字段是可选字段 String xml = "" + "<xml>" + "<appid>APPID</appid>" +//公众号ID "<device_info>WEB</device_info>" +//设备信息 "<detail>DETAIL</detail>" +//商品详情 "<body>BODY</body>" +//商品描述 "<mch_id>MERCHANT</mch_id>" +//微信给的商户ID "<nonce_str>FFHH</nonce_str>" +//32位随机字符串 "<notify_url><![CDATA[URL_TO]]></notify_url>" +//微信回调信息通知页面 "<openid>UserFrom</openid>" +//支付的用户ID "<fee_type>CNY</fee_type>" +//支付货币 "<spbill_create_ip>IP</spbill_create_ip>" +//用户IP "<time_start>START</time_start>" +//订单开始时间 "<time_expire>STOP</time_expire>" +//订单结束时间 "<goods_tag>WXG</goods_tag>" +//商品标记 "<product_id>GOODID</product_id>" +//商品ID "<limit_pay>no_credit</limit_pay>" +//支付范围,默认不支持信用卡支付 "<out_trade_no>PAY_NO</out_trade_no>" +//商城生成的订单号 "<total_fee>TOTAL</total_fee>" +//总金额 "<trade_type>TYPE</trade_type>" +//交易类型,JSAPI,NATIVE,APP,WAP "<sign>SIGN</sign>" +//加密字符串 "</xml>"; //生成订单起始时间,订单7天内有效 DateFormat df = new SimpleDateFormat("yyyyMMddhhmmss"); String start_time = df.format(new Date()); String stop_time = df.format(new Date().getTime() + 7 * 24 * 60 * 60 * 1000); //xml数据封装 //APP调起的时候,可能和公众号调起的商户号是不同的,所以需要分开设置 if ("APP".equalsIgnoreCase(type)) { xml = xml.replace("MERCHANT", app_mchid); xml = xml.replace("APPID", app_mchappid); } else { xml = xml.replace("MERCHANT", mchid); xml = xml.replace("APPID", mchappid); } xml = xml.replace("FFHH", randomNum); xml = xml.replace("DETAIL", detail); xml = xml.replace("BODY", desc); xml = xml.replace("URL_TO", wx_callback); xml = xml.replace("IP", ip); xml = xml.replace("START", start_time); xml = xml.replace("STOP", stop_time); xml = xml.replace("GOODID", goodSn); xml = xml.replace("PAY_NO", orderSn); xml = xml.replace("TOTAL", (int) relAmount + ""); xml = xml.replace("TYPE", type); if ("NATIVE".equalsIgnoreCase(type) || "APP".equalsIgnoreCase(type)) { xml = xml.replace("<openid>UserFrom</openid>", openid); } else { xml = xml.replace("UserFrom", openid); } // 4、加密 Map<String, String> map = new HashMap<String, String>(); map.put("device_info", "WEB"); map.put("detail", detail); map.put("body", desc); if ("APP".equalsIgnoreCase(type)) { map.put("mch_id", app_mchid); map.put("appid", app_mchappid); } else { map.put("mch_id", mchid); map.put("appid", mchappid); } map.put("nonce_str", randomNum); map.put("notify_url", wx_callback); map.put("fee_type", "CNY"); map.put("spbill_create_ip", ip); map.put("time_start", start_time); map.put("time_expire", stop_time); map.put("goods_tag", "WXG"); map.put("product_id", goodSn); map.put("limit_pay", "no_credit"); map.put("out_trade_no", orderSn); map.put("total_fee", (int) relAmount + ""); map.put("trade_type", type); String sign = SignatureUtils.signature(map, wx_key); xml = xml.replace("SIGN", sign); // 请求 String response = ""; try { //注意,此处的httputil一定发送请求的时候一定要注意中文乱码问题,中文乱码问题会导致在客户端加密是正确的,可是微信端返回的是加密错误 response = HttpUtils.post(wx_order, xml); } catch (Exception e) { return result; } //处理请求结果 XStream s = new XStream(new DomDriver()); s.alias("xml", WechatOrder.class); WechatOrder order = (WechatOrder) s.fromXML(response); if ("SUCCESS".equals(order.getReturn_code()) && "SUCCESS".equals(order.getResult_code())) { //支付成功的处理逻辑 } else { //支付失败的处理逻辑 } HashMap<String, String> back = new HashMap<String, String>(); //生成客户端调时需要的信息对象 //APP调起的时候,请注意,安卓端不能用驼峰法,所有的key必须使用小写 String time = Long.toString(System.currentTimeMillis()); back.put("appid", app_mchappid); back.put("timestamp", time); back.put("partnerid", app_mchid); back.put("noncestr", "5K8264ILTKCH16CQ2502SI8ZNMTM67VS"); back.put("prepayid", order.getPrepay_id()); back.put("package", "Sign=WXPay"); String sign2 = SignatureUtils.signature(back, wx_key); JSONObject jsonObject = new JSONObject(); jsonObject.put("appid", app_mchappid); jsonObject.put("timestamp", time); jsonObject.put("partnerid", app_mchid); jsonObject.put("noncestr", "5K8264ILTKCH16CQ2502SI8ZNMTM67VS"); jsonObject.put("prepayid", order.getPrepay_id()); //jsonObject.put("package", "Sign=WXPay"); jsonObject.put("sign", sign2); result.put("status", "success"); result.put("msg", "下单成功"); result.put("obj", jsonObject); return result; }
HttpUtils.post源码:
public static String post(String url, String str)throws Exception {// 处理请求地址URI uri = new URI(url);HttpPost post = new HttpPost(uri);post.setEntity(new StringEntity(str,"utf-8"));// 执行请求HttpResponse response = client.execute(post);if (response.getStatusLine().getStatusCode() == 200) {// 处理请求结果StringBuffer buffer = new StringBuffer();InputStream in = null;try {in = response.getEntity().getContent();BufferedReader reader = new BufferedReader(new InputStreamReader(in));String line = null;while ((line = reader.readLine()) != null) {buffer.append(line);}} finally {// 关闭流if (in != null)in.close();}return buffer.toString();} else {return null;}}PS:
微信回调,如果严谨的话可以加上金额的匹配,也就是除了验证签名,订单号,还有金额,总之约多约严谨
签名错误:如果报签名错误,除了按照上面微信提供的验证签名工具,如果都一样还报错,一般通过在商户平台重置支付key 会解决
微信执行回调函数:服务器一定要是80端口,给微信的url地址不能包含参数,不用限制get还是post请求
在初期调试时,建议多打日志信息,看看错误出在哪里,毕竟需要和客户端联调,这样会减少排错时间
阅读全文
1 0
- APP调起微信支付,JAVA服务端统一下单
- java微信app支付-统一下单
- 微信app支付-统一下单
- 解决微信App支付服务端,App上提示“商户支付下单id非法”
- 微信支付(java版本)_统一下单
- 微信支付(java版本)_统一下单
- 支付宝app支付java服务端
- 支付宝APP支付Java服务端
- 微信支付-----统一下单action
- 微信支付统一下单
- 微信支付之统一下单
- 【微信开发】支付-统一下单
- Java使用微信支付-发起统一下单支付接口
- 微信支付统一下单及调起支付接口的php接口实现 (可以用于app集成)
- 微信APP统一下单
- 微信APP支付服务端(JAVA)
- 微信APP支付Java服务端
- java服务端–支付宝APP支付接口
- 数组数据类型定义
- PHP反射机制
- 欢迎使用CSDN-markdown编辑器
- 制作开机LOGO就是这么简单!
- php设计模式总结-工厂模式
- APP调起微信支付,JAVA服务端统一下单
- Leetcode——92. Reverse Linked List II && 25. Reverse Nodes in k-Group
- kotlin代码教程
- 对象分割过程中,对没有赋label值的边界使用邻域查找的方式进行标记(1)
- Android.mk详解
- kotlin学习资源链接
- webpack配置中出现的问题
- 在Mac下反编译apk
- WannaCry感染文件恢复方法_企业再也不用愁了!