本文主要是介绍微信支付统一下单,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
- 估计大多数人和我一样吧,每次要使用腾讯的开发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+">");
- }
- }
- 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支付我就不说了,下一篇说一下支付结果通用通知
这篇关于微信支付统一下单的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!