微信扫码支付--模式一

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011134399/article/details/77412639

官方有关扫码支付的相关API

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4


业务流程说明:

(1)商户后台系统根据微信支付规定格式生成二维码(规则见下文),展示给用户扫码。

(2)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。

(3)微信支付系统收到客户端请求,发起对商户后台系统支付回调URL的调用。调用请求将带productid和用户的openid等参数,并要求商户系统返回交数据包,详细请见"本节3.1回调数据输入参数"

(4)商户后台系统收到微信支付系统的回调请求,根据productid生成商户系统的订单。

(5)商户系统调用微信支付【统一下单API】请求下单,获取交易会话标识(prepay_id)

(6)微信支付系统根据商户系统的请求生成预支付交易,并返回交易会话标识(prepay_id)。

(7)商户后台系统得到交易会话标识prepay_id(2小时内有效)。

(8)商户后台系统将prepay_id返回给微信支付系统。返回数据见"本节3.2回调数据输出参数"

(9)微信支付系统根据交易会话标识,发起用户端授权支付流程。

(10)用户在微信客户端输入密码,确认支付后,微信客户端提交支付授权。

(11)微信支付系统验证后扣款,完成支付交易。

(12)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。

(13)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。

(14)未收到支付通知的情况,商户后台系统调用【查询订单API】。

(15)商户确认订单已支付后给用户发货。




二维码生成调用第三方插件  

         谷歌的  zxing


生成二维码的代码如下:


protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		String appId = WxConfig.appId;
		String mchId = WxConfig.mchId;
		String apiKey = WxConfig.apiKey;
		String currTime = PayCommonUtil.getCurrTime();
		String strTime = currTime.substring(8, currTime.length());
		String strRandom = PayCommonUtil.buildRandom(4) + "";
		String nonce_str = strTime + strRandom;
		Calendar ca = Calendar.getInstance();
		ca.setTime(new Date());
		ca.add(Calendar.DATE, 1);
		SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
		packageParams.put("appid", appId);//公众帐号ID
		packageParams.put("mch_id", mchId);//商户号
		packageParams.put("time_stamp",currTime);//时间戳
		packageParams.put("nonce_str", nonce_str); //随机字符串
		packageParams.put("product_id", nonce_str);//商品ID
				
		String sign = PayCommonUtil.createSign("UTF-8", packageParams, apiKey);
		packageParams.put("sign", sign);
        
		
		String requestXML = ToUrlParams(packageParams);
        
		//String payurl = "weixin://wxpay/bizpayurl?" + requestXML;
		String payurl = "weixin://wxpay/bizpayurl?sign=" + sign+"&appid="+appId+"&mch_id="+mchId+"&time_stamp="+currTime+"&nonce_str="+nonce_str+"&product_id="+nonce_str;
		
        try {
			BitMatrix bitMatrix = new MultiFormatWriter().encode(payurl,BarcodeFormat.QR_CODE, defaultWidthAndHeight, 200);
			OutputStream out = response.getOutputStream();
			MatrixToImageWriter.writeToStream(bitMatrix, "png", out);//输出二维码
            out.flush();
            out.close();
            
        } catch (WriterException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


回调方法


public static String PAY_API = "https://api.mch.weixin.qq.com/pay/unifiedorder";  -- 调用统一下单方法

	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		
		req.setCharacterEncoding("UTF-8");
		resp.setContentType("application/json");
		resp.setCharacterEncoding("UTF-8");
		
		
		String apiKey = WxConfig.apiKey;
	   
		/**
	    * 获取用户扫描二维码后,微信返回的信息
	    */
	   InputStream inStream = req.getInputStream();
	   ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
	   byte[] buffer = new byte[1024];
	   int len = 0;
	   while ((len = inStream.read(buffer)) != -1) {
	       outSteam.write(buffer, 0, len);
	   }
	   outSteam.close();
	   inStream.close();
	   String result  = new String(outSteam.toByteArray(),"utf-8");
	
		
		
	   /**
	    * 调用统一下单
	    * 
	    * */
		SortedMap<Object, Object> reParams = new TreeMap<Object, Object>();
		try {
			reParams = XMLUtil.xmlConvertToSortedMap(result);
		} catch (JDOMException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		
		String retXml="";//反馈给微信服务器 
		
		
	
		//验证签名
		if (PayCommonUtil.isTenpaySign("UTF-8", reParams, apiKey)) { 
			
				//统一下单
			    String appId = WxConfig.appId;
				String mchId = WxConfig.mchId;
				String currTime = PayCommonUtil.getCurrTime();
				String strTime = currTime.substring(8, currTime.length());
				String strRandom = PayCommonUtil.buildRandom(4) + "";
				String nonce_str = strTime + strRandom;
				SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
				packageParams.put("appid", appId);
				packageParams.put("openid", reParams.get("openid"));
				packageParams.put("mch_id", mchId);
				packageParams.put("nonce_str", nonce_str);
				packageParams.put("body", "测试");
				packageParams.put("out_trade_no",  reParams.get("product_id"));
				packageParams.put("total_fee", "1");
				packageParams.put("spbill_create_ip", "127.0.0.1");
				packageParams.put("trade_type", "NATIVE");
				packageParams.put("notify_url", "http://netbar1.legentec.com/riskmanage/unifiedOrder");//支付后返回结果
				String sign = PayCommonUtil.createSign("UTF-8", packageParams, apiKey);
				packageParams.put("sign", sign);
				String requestXML = PayCommonUtil.getRequestXml(packageParams);
			    System.out.println("请求xml::::"+requestXML);
				String resXml = HttpUtil.postData(PAY_API, requestXML);
			    System.out.println("返回xml::::"+resXml);
		
	    
	    
			    SortedMap<Object, Object> sortParams = new TreeMap<Object, Object>();
				try {
					sortParams = XMLUtil.xmlConvertToSortedMap(resXml);
				} catch (JDOMException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} 
	    
		   
		      //验签 
		      if (PayCommonUtil.isTenpaySign("UTF-8", sortParams, apiKey)) { 
		    	  
		    	 String resultCode = (String) sortParams.get("result_code");
		    	  
		    	if(resultCode.equals("SUCCESS")) {
		    		// 统一下单返回的参数 
			        String prepay_id = (String)sortParams.get("prepay_id");//交易会话标识 2小时内有效 
			        SortedMap<Object,Object> resParams = new TreeMap<Object,Object>();  
			        resParams.put("return_code", "SUCCESS"); // 必须 
			        resParams.put("return_msg", "OK"); 
			        resParams.put("appid", appId); // 必须 
			        resParams.put("mch_id", mchId); 
			        resParams.put("nonce_str", nonce_str); // 必须 
			        resParams.put("prepay_id", prepay_id); // 必须 
			        resParams.put("result_code", "SUCCESS"); // 必须 
			        resParams.put("err_code_des", "OK"); 
			        String sign1 = PayCommonUtil.createSign("UTF-8", resParams,apiKey);  
			        resParams.put("sign", sign1); //签名 
			          
			        retXml = PayCommonUtil.getRequestXml(resParams); 
		    		
		    	} else{
		    		
		    		retXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" 
				            + "<return_msg><![CDATA["+(String) sortParams.get("err_code_des")+"]]></return_msg>" + "</xml> ";  
		    	}
		    	
		          
		      }else{ 
		        
		    	  retXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" 
		            + "<return_msg><![CDATA[222222]]></return_msg>" + "</xml> ";  
		      } 
	        
	    }else{ 
	    	retXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" 
	          + "<return_msg><![CDATA[签名错误]]></return_msg>" + "</xml> ";  
	    	
	    	
	    } 
	   
		
		  //------------------------------      //处理业务完毕      //------------------------------      
		BufferedOutputStream out = new BufferedOutputStream(          
				resp.getOutputStream());      
		out.write(retXml.getBytes());     
		out.flush();      
		out.close();  
		super.doPost(req, resp);
	}


使用到的工具类

PayCommonUtil


package com.legentec.wechat.pay;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

@SuppressWarnings("rawtypes")
public class PayCommonUtil
{

	/**
	 * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
	 * 
	 * @return boolean
	 */
	public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY)
	{
		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=" + 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);
	}

	/**
	 * @author
	 * @date 2016-4-22
	 * @Description:sign签名
	 * @param characterEncoding
	 * 编码格式
	 * @param parameters
	 * 请求参数
	 * @return
	 */
	public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY)
	{
		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 (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;
	}

	/**
	 * @author
	 * @date 2016-4-22
	 * @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) || "sign".equalsIgnoreCase(k))
			{
				sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
			}
			else
			{
				sb.append("<" + k + ">" + v + "</" + k + ">");
			}
		}
		sb.append("</xml>");
		return sb.toString();
	}

	/**
	 * 取出一个指定长度大小的随机正整数.
	 * 
	 * @param length
	 * int 设定所取出随机数的长度。length小于11
	 * @return int 返回生成的随机数。
	 */
	public static int buildRandom(int length)
	{
		int num = 1;
		double random = Math.random();
		if (random < 0.1)
		{
			random = random + 0.1;
		}
		for (int i = 0; i < length; i++)
		{
			num = num * 10;
		}
		return (int) ((random * num));
	}

	/**
	 * 获取当前时间 yyyyMMddHHmmss
	 * 
	 * @return String
	 */
	public static String getCurrTime()
	{
		Date now = new Date();
		SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
		String s = outFormat.format(now);
		return s;
	}
}

XMLUtil


package com.legentec.wechat.pay;

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 java.util.SortedMap;
import java.util.TreeMap;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
@SuppressWarnings("rawtypes")
public class XMLUtil
{
	/**
	 * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
	 * 
	 * @param strxml
	 * @return
	 * @throws JDOMException
	 * @throws IOException
	 */
	
	@SuppressWarnings("unchecked")
	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;
	}
	
	
	
	public static SortedMap xmlConvertToSortedMap(String strxml) throws JDOMException, IOException
	{
		strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");

		if (null == strxml || "".equals(strxml))
		{
			return null;
		}

		SortedMap<Object, Object> m = new TreeMap<Object, Object>();

		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();
	}

}


使用JAR包


支付完成后调用统一下配置的地址(回调地址一定为POST请求


package com.wxpay;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.jdom.JDOMException;

import com.legentec.wechat.WxConfig;
import com.legentec.wechat.pay.PayCommonUtil;
import com.legentec.wechat.pay.XMLUtil;

public class unifiedOrder extends HttpServlet {


	protected static String getReturnXML(String return_code, String return_msg) {
		return "<xml><return_code><![CDATA[" + return_code
				+ "]]></return_code><return_msg><![CDATA[" + return_msg
				+ "]]></return_msg></xml>";
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		// 读取参数
		InputStream inputStream;
		StringBuffer sb = new StringBuffer();
		inputStream = req.getInputStream();
		String s;
		BufferedReader in = new BufferedReader(new InputStreamReader(
				inputStream, "UTF-8"));
		while ((s = in.readLine()) != null) {
			sb.append(s);
		}
		in.close();
		inputStream.close();
		SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
		try {
			packageParams = XMLUtil.xmlConvertToSortedMap(sb.toString());
		} catch (JDOMException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		// 账号信息
		String key = WxConfig.apiKey;
		String resXml = "";
		// 反馈给微信服务器
		// 判断签名是否正确
		if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, key)) {
			// ------------------------------ // 处理业务开始 //
			// ------------------------------
			if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
				// 这里是支付成功 ////////// 执行自己的业务逻辑////////////////
				String mch_id = (String) packageParams.get("mch_id");
				String openid = (String) packageParams.get("openid");
				String is_subscribe = (String) packageParams
						.get("is_subscribe");
				String out_trade_no = (String) packageParams
						.get("out_trade_no");
				String total_fee = (String) packageParams.get("total_fee");
				
				// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
				resXml = "<xml>"
						+ "<return_code><![CDATA[SUCCESS]]></return_code>"
						+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
			} else {
				
				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> ";
		} 
		
	    
		// ------------------------------ // 处理业务完毕 //
		
		BufferedOutputStream out = new BufferedOutputStream(          
				resp.getOutputStream());      
		out.write(resXml.getBytes());     
		out.flush();      
		out.close(); 

		super.doGet(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		doGet(req, resp);
		super.doPost(req, resp);
	}
}


猜你喜欢

转载自blog.csdn.net/u011134399/article/details/77412639