java微信app支付-统一下单
2016-03-17 17:07
776 查看
估计大多数人和我一样吧,每次要使用腾讯的开发api,都有一种想上吊的感觉,尤其是微信支付和支付宝对比起来文档确实差距还是有点大的
关于微信支付的申请我就不多说了,网上有很多,我们直接来说一下微信支付
微信支付是以下步骤(官方文档说明 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_3)
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
步骤3要注意千万不要按照他给的签名字段进行前面,我就被坑了一天
appid
partnerid
prepayid
package
noncestr
timestamp
sign
签名字段一定不要按照文档的驼峰写法,应该是全部小写,就像上面
步骤4:商户APP调起微信支付。api参见本章节【app端开发步骤说明】
步骤5:商户后台接收支付通知。api参见【支付结果通知API】
步骤6:商户后台查询支付结果。,api参见【查询订单API】
接下来我们就开始讲解统一下单
请求URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
参数
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
应用ID | appid | 是 | String(32) | wxd678efh567hg6787 | 微信开放平台审核通过的应用APPID |
商户号 | mch_id | 是 | String(32) | 1230000109 | 微信支付分配的商户号 |
设备号 | device_info | 否 | String(32) | 013467007045764 | 终端设备号(门店号或收银设备ID),默认请传"WEB" |
随机字符串 | nonce_str | 是 | String(32) | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 随机字符串,不长于32位。推荐随机数生成算法 |
签名 | sign | 是 | String(32) | C380BEC2BFD727A4B6845133519F3AD6 | 签名,详见签名生成算法 |
商品描述 | body | 是 | String(128) | Ipad mini 16G 白色 | 商品或支付单简要描述 |
商品详情 | detail | 否 | String(8192) | Ipad mini 16G 白色 | 商品名称明细列表 |
附加数据 | attach | 否 | String(127) | 深圳分店 | 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 |
商户订单号 | out_trade_no | 是 | String(32) | 20150806125346 | 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号 |
货币类型 | fee_type | 否 | String(16) | CNY | 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 |
总金额 | total_fee | 是 | Int | 888 | 订单总金额,单位为分,详见支付金额 |
终端IP | spbill_create_ip | 是 | String(16) | 123.12.12.123 | 用户端实际ip |
交易起始时间 | time_start | 否 | String(14) | 20091225091010 | 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 |
交易结束时间 | time_expire | 否 | String(14) | 20091227091010 | 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则 注意:最短失效时间间隔必须大于5分钟 |
商品标记 | goods_tag | 否 | String(32) | WXG | 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠 |
通知地址 | notify_url | 是 | String(256) | http://www.weixin.qq.com/wxpay/pay.php | 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。 |
交易类型 | trade_type | 是 | String(16) | APP | 支付类型 |
指定支付方式 | limit_pay | 否 | String(32) | no_credit | no_credit--指定不能使用信用卡支付 |
public class PayCommonUtil { //微信参数配置 public static String API_KEY=""; public static String APPID=""; public static String MCH_ID=""; //随机字符串生成 public static String getRandomString(int length) { //length表示生成字符串的长度 String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 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(); } //请求xml组装 public static String getRequestXml(SortedMap<String,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 key = (String)entry.getKey(); String value = (String)entry.getValue(); if ("attach".equalsIgnoreCase(key)||"body".equalsIgnoreCase(key)||"sign".equalsIgnoreCase(key)) { sb.append("<"+key+">"+"<![CDATA["+value+"]]></"+key+">"); }else { sb.append("<"+key+">"+value+"</"+key 4000 +">"); } } sb.append("</xml>"); return sb.toString(); } //生成签名 public static String createSign(String characterEncoding,SortedMap<String,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=" + API_KEY); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } //请求方法 public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) { try { URL url = new URL(requestUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 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) { System.out.println("连接超时:{}"+ ce); } catch (Exception e) { System.out.println("https请求异常:{}"+ e); } return null; } //退款的请求方法 public static String httpsRequest2(String requestUrl, String requestMethod, String outputStr) throws Exception { KeyStore keyStore = KeyStore.getInstance("PKCS12"); StringBuilder res = new StringBuilder(""); FileInputStream instream = new FileInputStream(new File("/home/apiclient_cert.p12")); try { keyStore.load(instream, "".toCharArray()); } finally { instream.close(); } // Trust own CA and all self-signed certs SSLContext sslcontext = SSLContexts.custom() .loadKeyMaterial(keyStore, "1313329201".toCharArray()) .build(); // Allow TLSv1 protocol only SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); CloseableHttpClient httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf) .build(); try { HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund"); httpost.addHeader("Connection", "keep-alive"); httpost.addHeader("Accept", "*/*"); httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); httpost.addHeader("Host", "api.mch.weixin.qq.com"); httpost.addHeader("X-Requested-With", "XMLHttpRequest"); httpost.addHeader("Cache-Control", "max-age=0"); httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); StringEntity entity2 = new StringEntity(outputStr ,Consts.UTF_8); httpost.setEntity(entity2); System.out.println("executing request" + httpost.getRequestLine()); CloseableHttpResponse response = httpclient.execute(httpost); try { HttpEntity entity = response.getEntity(); System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); if (entity != null) { System.out.println("Response content length: " + entity.getContentLength()); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent())); String text;res.append(text); while ((text = bufferedReader.readLine()) != null) { res.append(text); System.out.println(text); } } EntityUtils.consume(entity); } finally { response.close(); } } finally { httpclient.close(); } return res.toString(); } //xml解析 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 = getChildrenText(children); } m.put(k, v); } //关闭流 in.close(); return m; } 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(getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } }
统一下单
参数说明
sn订单号
totalAmount支付金额
description产品描述
public static Map<String, String> weixinPrePay(String sn,BigDecimal totalAmount, String description, HttpServletRequest request) { Setting setting = SettingUtils.get(); SortedMap<String, Object> parameterMap = new TreeMap<String, Object>(); parameterMap.put("appid", PayCommonUtil.APPID); parameterMap.put("mch_id", PayCommonUtil.MCH_ID); parameterMap.put("nonce_str", PayCommonUtil.getRandomString(32)); parameterMap.put("body", StringUtils.abbreviate(description.replaceAll( "[^0-9a-zA-Z\\u4e00-\\u9fa5 ]", ""), 600)); parameterMap.put("out_trade_no", sn); parameterMap.put("fee_type", "CNY"); System.out.println("jiner"); BigDecimal total = totalAmount.multiply(new BigDecimal(100)); java.text.DecimalFormat df=new java.text.DecimalFormat("0"); parameterMap.put("total_fee", df.format(total)); System.out.println("jiner2"); parameterMap.put("spbill_create_ip", request.getRemoteAddr()); parameterMap.put("notify_url", "http://xxx.com"); parameterMap.put("trade_type", "APP"); System.out.println(""); String sign = PayCommonUtil.createSign("UTF-8", parameterMap); System.out.println("jiner2"); parameterMap.put("sign", sign); String requestXML = PayCommonUtil.getRequestXml(parameterMap); System.out.println(requestXML); String result = PayCommonUtil.httpsRequest( "https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", requestXML); System.out.println(result); Map<String, String> map = null; try { map = PayCommonUtil.doXMLParse(result); } catch (JDOMException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return map; }
获取prepay_id,二次签名发送结果给app,此方法结合业务自己优化一下,这里只是举个例子
public static String createSignAgain(HttpServletRequest request) Map<String, String> map = weixinPrePay(payment.getSn(), payment.getAmount(),description, request); JSONObject jsonObject = new JSONObject(); SortedMap<String, Object> parameterMap = new TreeMap<String, Object>(); parameterMap.put("appid", PayCommonUtil.APPID); parameterMap.put("partnerid", PayCommonUtil.MCH_ID); parameterMap.put("prepayid", map.get("prepay_id")); parameterMap.put("package", "Sign=WXPay"); parameterMap.put("noncestr", PayCommonUtil.getRandomString(32)); parameterMap.put("timestamp", System.currentTimeMillis()); String sign = PayCommonUtil.createSign("UTF-8", parameterMap); parameterMap.put("sign", sign); jsonObject.put("parameterMap",parameterMap); return jsonObject.toString(); }
到此统一下下单完成,关于app支付我就不说了,下一篇说一下支付结果通用通知
相关文章推荐
- 社交巨头三国杀:微信、WhatsApp、Line到底有啥区别?
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- 微信悄悄升级群聊功能:个人微信营销号的福音
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序