微信支付公的众号支付和扫码支付

公众号支付是手机端的微信公众号H5页面支付,这种支付方式必须是在微信内置浏览器发起。

扫码支付分为模式一和模式二,模式一主要为线下服务,该模式是先扫码,再生成订单,商户先为自己的商品生成二维码连接,然后用户扫码之后决定是否购买,二维码无过期时间,比如自动售卖机大多采用这种模式;模式二主要为线上电商服务,用户选择商品后生成订单,根据订单生成二维码,然后支付,该二维码为临时二维码。


开发流程

一、授权目录

官方文档说必须是精确目录,其实是二级或三级目录就可以了,太精确的可能还会出现不识别的情况。如果是扫码支付模式一还需要设置扫码支付回调URL

微信支付公的众号支付和扫码支付



二.统一下单

注意传入参数不要为null,尽量不要是空字符串,如果在没有抛出Exception的情况下支付失败,十有八九是参数导致的签名有问题,微信支付的签名规定的特别严格,必须按照微信给的规则来,建议第一次先用demo提供的签名方法,以后可以再修改。这里的appid就是支付所在的公众号的appid,openId是用户对应当前公众号的openId。

1、扫码支付模式二

扫码支付比较简单,可以直接通过url发起,传入统一下单参数,生成扫码支付的url。扫码支付的trade_type为NATIVE。

[java] view plain copy
  1. public void getCodeUrl(@PathVariable(value="tradeNo")String tradeNo, HttpServletRequest request,  
  2.             HttpServletResponse response) throws Exception {  
  3.         //根据订单号获取订单详情  
  4.         OrderProduceBean order = reservationCarService.getTradebyNo(tradeNo);  
  5.           
  6.         // 附加数据 原样返回  
  7.         String attach = "attach";  
  8.         // 总金额以分为单位,不带小数点  
  9.         String totalFee = TenpayUtil.getMoney(order.getTotalFee().toString());  
  10.         // 订单生成的机器 IP  
  11.         String spbill_create_ip = IpUtil.getIpAddr(request);  
  12.         // 这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等。  
  13.         String notify_url = TenPayConfig.notifyUrl;  
  14.         String trade_type = "NATIVE";  
  15.         // 商户号  
  16.         String mch_id = TenPayConfig.partner;  
  17.         // 随机字符串  
  18.         String nonce_str = TenpayUtil.getNonceStr();  
  19.         // 商品描述根据情况修改  
  20.         String body = order.getBody();  
  21.         // 商户订单号  
  22.         String out_trade_no = order.getOutTradeNo();  
  23.   
  24.         SortedMap<String, String> packageParams = new TreeMap<String, String>();  
  25.         packageParams.put("appid", TenPayConfig.appid);  
  26.         packageParams.put("mch_id", mch_id);  
  27.         packageParams.put("nonce_str", nonce_str);  
  28.         packageParams.put("body", body);  
  29.         packageParams.put("attach", attach);  
  30.         packageParams.put("out_trade_no", out_trade_no);  
  31.   
  32.         packageParams.put("total_fee", totalFee);  
  33.         packageParams.put("spbill_create_ip", spbill_create_ip);  
  34.         packageParams.put("notify_url", notify_url);  
  35.   
  36.         packageParams.put("trade_type", trade_type);  
  37.   
  38.         RequestHandler reqHandler = new RequestHandler(nullnull);  
  39.         reqHandler.init(TenPayConfig.appid, TenPayConfig.appsecret,  
  40.                 TenPayConfig.partnerkey);  
  41.   
  42.         String sign = reqHandler.createSign(packageParams);  
  43.         String xml = "<xml>" + "<appid>" + TenPayConfig.appid + "</appid>"  
  44.                 + "<mch_id>" + mch_id + "</mch_id>" + "<nonce_str>" + nonce_str  
  45.                 + "</nonce_str>" + "<sign>" + sign + "</sign>"  
  46.                 + "<body><![CDATA[" + body + "]]></body>" + "<out_trade_no>"  
  47.                 + out_trade_no + "</out_trade_no>" + "<attach>" + attach  
  48.                 + "</attach>" + "<total_fee>" + totalFee + "</total_fee>"  
  49.                 + "<spbill_create_ip>" + spbill_create_ip  
  50.                 + "</spbill_create_ip>" + "<notify_url>" + notify_url  
  51.                 + "</notify_url>" + "<trade_type>" + trade_type  
  52.                 + "</trade_type>" + "</xml>";  
  53.         System.out.println(xml);  
  54.         String code_url = "";  
  55.         String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";  
  56.   
  57.         code_url = new TenPayCore().getCodeUrl(createOrderURL, xml);//调用统一下单接口  
  58.         if (code_url == null || code_url.equalsIgnoreCase("")) {  
  59.             logger.debug("****************************trade has closed or no this trade in tencentPay");  
  60.             response.sendError(404);  
  61.             return;  
  62.         }  
  63.   
  64.         GenerateQrCodeUtil.encodeQrcode(code_url, response);  
  65.     }  


2.公众号支付

公众号支付首先通过H5调起支付api,微信生成订单,然后开发者获取预支付id,最后由用户确认支付。

①H5调起JSAPI。

调起有两种方式,一种是WeixinJSBridge.invoke(),另一种是最新的微信JSAPI,两种方式均可,官方文档用的第一种,使用第一种方式首先要引入http://res.wx.qq.com/open/js/jweixin-1.0.0.js。

[javascript] view plain copy
  1. function toPay(){  
  2.     $.ajax({  
  3.         url : URL,  
  4.         type : "GET",  
  5.         dataType : 'json',  
  6.         success : function(data) {  
  7.             WeixinJSBridge.invoke('getBrandWCPayRequest', {  
  8.                 "appId" : data.appId, //公众号名称,由商户传入       
  9.                 "timeStamp" : data.timeStamp, //时间戳,自1970年以来的秒数       
  10.                 "nonceStr" : data.nonceStr, //随机串       
  11.                 "package" : data.package,  
  12.                 "signType" : data.signType, //微信签名方式:       
  13.                 "paySign" : data.paySign  
  14.             //微信签名   
  15.             }, function(res) {  
  16.                 if (res.err_msg == "get_brand_wcpay_request:ok") {  
  17.                 } // 使用以上方式判断前端返回:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。   
  18.                 else{  
  19.                     //res.err_msg;  
  20.                 }  
  21.             });  
  22.         }  
  23.     });  
  24. }     


②获取预支付id

这个api和扫码支付的api差不多,就是trade为JSAPI不一样。

[java] view plain copy
  1. public void  TencentPayController(@RequestParam("orderno")String orderno,HttpServletRequest request,  
  2.             HttpServletResponse response) throws IOException {  
  3.         //根据订单号查询订单  
  4.         OrderProduceBean order=reservationCarService.getTradebyNo(orderno);  
  5.           
  6.         String appid = TenPayConfig.appid;  
  7.         String openId =order.getOpenId();  
  8.         // 订单号  
  9.         String orderId = order.getOutTradeNo();  
  10.         // 附加数据 原样返回  
  11.         String attach = "attach";  
  12.         // 总金额以分为单位,不带小数点  
  13.         String totalFee = TenpayUtil.getMoney(order.getTotalFee().toString());  
  14.           
  15.         // 订单生成的机器 IP  
  16.         String spbill_create_ip = IpUtil.getIpAddr(request);;  
  17.         // 这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等。  
  18.         String notify_url = TenPayConfig.notifyUrl;  
  19.         String trade_type = "JSAPI";  
  20.   
  21.         // ---必须参数  
  22.         // 商户号  
  23.         String mch_id = TenPayConfig.partner;  
  24.         // 随机字符串  
  25.         String nonce_str = TenpayUtil.getNonceStr();  
  26.   
  27.         // 商品描述根据情况修改  
  28.         String body = order.getBody();  
  29.   
  30.         // 商户订单号  
  31.         String out_trade_no = orderId;  
  32.   
  33.         SortedMap<String, String> packageParams = new TreeMap<String, String>();  
  34.         packageParams.put("appid", appid);  
  35.         packageParams.put("mch_id", mch_id);  
  36.         packageParams.put("nonce_str", nonce_str);  
  37.         packageParams.put("body", body);  
  38.         packageParams.put("attach", attach);  
  39.         packageParams.put("out_trade_no", out_trade_no);  
  40.   
  41.         packageParams.put("total_fee", totalFee);  
  42.         packageParams.put("spbill_create_ip", spbill_create_ip);  
  43.         packageParams.put("notify_url", notify_url);  
  44.   
  45.         packageParams.put("trade_type", trade_type);  
  46.         packageParams.put("openid", openId);  
  47.   
  48.         RequestHandler reqHandler = new RequestHandler(nullnull);  
  49.         reqHandler.init(appid, TenPayConfig.appsecret,TenPayConfig.partnerkey);  
  50.   
  51.         String sign = reqHandler.createSign(packageParams);  
  52.         String xml = "<xml>" + "<appid>" + appid + "</appid>" + "<mch_id>"  
  53.                 + mch_id + "</mch_id>" + "<nonce_str>" + nonce_str  
  54.                 + "</nonce_str>" + "<sign>" + sign + "</sign>"  
  55.                 + "<body><![CDATA[" + body + "]]></body>"   
  56.                 + "<out_trade_no>" + out_trade_no  
  57.                 + "</out_trade_no>" + "<attach>" + attach + "</attach>"  
  58.                 + "<total_fee>" + totalFee + "</total_fee>"  
  59.                 + "<spbill_create_ip>" + spbill_create_ip  
  60.                 + "</spbill_create_ip>" + "<notify_url>" + notify_url  
  61.                 + "</notify_url>" + "<trade_type>" + trade_type  
  62.                 + "</trade_type>" + "<openid>" + openId + "</openid>"  
  63.                 + "</xml>";  
  64.         String prepay_id = "";  
  65.         String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";  
  66.                   
  67.         prepay_id = new TenPayCore().getPayNo(createOrderURL, xml);  
  68.         logger.debug("********************************************");  
  69.         logger.debug("prepay_id :" + prepay_id);  
  70.           
  71.           
  72.         //获取prepay_id后,拼接最后请求支付所需要的package    
  73.         SortedMap<String, String> finalpackage = new TreeMap<String, String>();  
  74.         String timestamp = Sha1Util.getTimeStamp();  
  75.         String packages = "prepay_id="+prepay_id;  
  76.         finalpackage.put("appId", appid);    
  77.         finalpackage.put("timeStamp", timestamp);    
  78.         finalpackage.put("nonceStr", nonce_str);    
  79.         finalpackage.put("package", packages);    
  80.         finalpackage.put("signType""MD5");  
  81.         //要签名  
  82.         String finalsign = reqHandler.createSign(finalpackage);  
  83.         finalpackage.put("paySign", finalsign);  
  84.           
  85.         String finaPackage = "\"appId\":\"" + appid + "\",\"timeStamp\":\"" + timestamp  
  86.         + "\",\"nonceStr\":\"" + nonce_str + "\",\"package\":\""  
  87.         + packages + "\",\"signType\" : \"MD5" + "\",\"paySign\":\""  
  88.         + finalsign + "\"";  
  89.           
  90.         logger.debug("********************************************");  
  91.         logger.debug("V3 jsApi package:" + finaPackage);  
  92.           
  93.         if(prepay_id.length()>0){  
  94.             this.callBack(response, request,this.gson.toJson(finalpackage));  
  95.             return;  
  96.         }else{  
  97.             this.callBack(response, request,"{\"error\":\"prepay_id is null\"}");  
  98.             return;  
  99.         }         
  100.           
  101.     }  


3.支付回调

该回调方法的路径就是获取预支付id中的参数notify_url,用来处理支付完成后的业务逻辑,注意一定要检查return_code和result_code是否都为success,另外一定要给微信返回success信息.

[java] view plain copy
  1. public void notifyPay(HttpServletRequest request,  
  2.         HttpServletResponse response) throws IOException {  
  3.     // 示例报文  
  4.     String inputLine;  
  5.     String notityXml = "";  
  6.     String resXml = "";  
  7.     try {  
  8.         while ((inputLine = request.getReader().readLine()) != null) {  
  9.             notityXml += inputLine;  
  10.         }  
  11.         request.getReader().close();  
  12.     } catch (Exception e) {  
  13.         e.printStackTrace();  
  14.     }  
  15.   
  16.     Map<String, String> m = TenpayUtil.parseXmlToList(notityXml);  
  17.     OrderTenPayBean orderTen = new OrderTenPayBean();  
  18.     orderTen.setAppid(m.get("appid").toString());  
  19.     orderTen.setBankType(m.get("bank_type").toString());  
  20.     orderTen.setCashFee(m.get("cash_fee").toString());  
  21.     orderTen.setFeeType(m.get("fee_type").toString());  
  22.     orderTen.setIsSubscribe(m.get("is_subscribe").toString());  
  23.     orderTen.setMchId(m.get("mch_id").toString());  
  24.     orderTen.setNonceStr(m.get("nonce_str").toString());  
  25.     orderTen.setOpenid(m.get("openid").toString());  
  26.     orderTen.setOutTradeNo(m.get("out_trade_no").toString());  
  27.     orderTen.setResultCode(m.get("result_code").toString());  
  28.     orderTen.setReturnCode(m.get("return_code").toString());  
  29.     orderTen.setSign(m.get("sign").toString());  
  30.     orderTen.setTimeEnd(m.get("time_end").toString());  
  31.     orderTen.setTotalFee(m.get("total_fee").toString());  
  32.     orderTen.setTradeType(m.get("trade_type").toString());  
  33.     orderTen.setTransactionId(m.get("transaction_id").toString());  
  34.   
  35.     if ("SUCCESS".equals(orderTen.getResultCode()) && "SUCCESS".equals(orderTen.getReturnCode())<span style="font-family: Arial, Helvetica, sans-serif;">) { //必须判断</span>  
  36.         //TODO   信息入库,修改订单状态  
  37.           
  38.         // 支付成功,必须给微信返回此信息,否则微信会一直调用此回调方法  
  39.         resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"  
  40.                 + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";  
  41.     } else {  
  42.         resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"  
  43.                 + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";  
  44.     }  
  45.     this.callBack(response, request, resXml);  
  46. }  

4.退款

退款需要把商户证书apiclient_cert.p12放到指定目录下,因为有证书的情况下退款不需要密码,一定要注意业务请求的权限问题

[java] view plain copy
  1. public void refusePay(HttpServletResponse response,HttpServletRequest request,@PathVariable(value="outTradeNo")String outTradeNo) throws Exception {  
  2.       
  3.     OrderProduceBean tradebyNo = reservationCarService.getTradebyNo(outTradeNo);  
  4.       
  5.     SortedMap<String, String> parameters = new TreeMap<String, String>();  
  6.     parameters.put("appid", TenPayConfig.appid);  
  7.     parameters.put("mch_id", TenPayConfig.partner);  
  8.     parameters.put("nonce_str", TenpayUtil.getNonceStr());  
  9.     parameters.put("out_trade_no", tradebyNo.getOutTradeNo());  
  10.     parameters.put("out_refund_no", tradebyNo.getOutTradeNo()); // 我们自己设定的退款申请号,约束为UK  
  11.     parameters.put("total_fee", TenpayUtil.getMoney(tradebyNo.getTotalFee().toString())); // 单位为分  
  12.     parameters.put("refund_fee", TenpayUtil.getMoney(tradebyNo.getTotalFee().toString())); // 单位为分  
  13.     parameters.put("op_user_id", TenPayConfig.partner);  
  14.       
  15.     RequestHandler reqHandler = new RequestHandler(nullnull);  
  16.     reqHandler.init(TenPayConfig.appid, TenPayConfig.appsecret,  
  17.             TenPayConfig.partnerkey);  
  18.   
  19.     String sign = reqHandler.createSign(parameters);  
  20.     parameters.put("sign", sign);  
  21.   
  22.     String reuqestXml = TenpayUtil.getRequestXml(parameters);  
  23.       
  24.     KeyStore keyStore  = KeyStore.getInstance("PKCS12");  
  25.     FileInputStream instream = new FileInputStream(new File(Resources.getString("cert_url")));  
  26.     try {  
  27.         keyStore.load(instream, TenPayConfig.partner.toCharArray());  
  28.     } finally {  
  29.         instream.close();  
  30.     }  
  31.   
  32.     // Trust own CA and all self-signed certs  
  33.     SSLContext sslcontext = SSLContexts.custom()  
  34.             .loadKeyMaterial(keyStore, TenPayConfig.partner.toCharArray())  
  35.             .build();  
  36.     // Allow TLSv1 protocol only  
  37.     SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(  
  38.             sslcontext,  
  39.             new String[] { "TLSv1" },  
  40.             null,  
  41.             SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);  
  42.     CloseableHttpClient httpclient = HttpClients.custom()  
  43.             .setSSLSocketFactory(sslsf)  
  44.             .build();  
  45.     try {  
  46.         HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");  
  47.         System.out.println("executing request" + httpPost.getRequestLine());  
  48.         StringEntity reqEntity = new StringEntity(reuqestXml);  
  49.         // 设置类型  
  50.         reqEntity.setContentType("application/x-www-form-urlencoded");  
  51.         httpPost.setEntity(reqEntity);  
  52.         CloseableHttpResponse reqs = httpclient.execute(httpPost);  
  53.         try {  
  54.             HttpEntity entity = reqs.getEntity();  
  55.             if (entity != null) {  
  56.                 System.out.println("Response content length: " + entity.getContentLength());  
  57.                 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));  
  58.                 String returnXml = "";  
  59.                 String text;  
  60.                 while ((text = bufferedReader.readLine()) != null) {  
  61.                     returnXml += text;  
  62.                 }  
  63.                 //微信退款返回的参数转换为Map  
  64.                 Map<String, String> resultMaP = TenpayUtil.parseXmlToList(returnXml);  
  65.                 this.callBack(response, request, resultMaP.toString());  
  66.                 //TODO   信息入库             
  67.                   
  68.             }  
  69.             EntityUtils.consume(entity);  
  70.         } finally {  
  71.             reqs.close();  
  72.         }  
  73.     } finally {  
  74.         httpclient.close();  
  75.     }  
  76.   
  77. }  

PS:代码仅供参考!