微信H5支付详解(案列) 秒懂

  • 1.了解微信H5支付 流程

1、用户在商户侧完成下单,使用微信支付进行支付

2、由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB

3、统一下单接口返回支付相关参数给商户后台,如支付跳转url(参数名“mweb_url”),商户通过mweb_url调起微信支付中间页

4、中间页进行H5权限的校验,安全性检查(此处常见错误请见下文)

5、如支付成功,商户后台会接收到微信侧的异步通知

6、用户在微信支付收银台完成支付或取消支付,返回商户页面(默认为返回支付发起页面)

7、商户在展示页面,引导用户主动发起支付结果的查询

8,9、商户后台判断是否接到收微信侧的支付结果通知,如没有,后台调用我们的订单查询接口确认订单状态

10、展示最终的订单支付结果给用户


 

  • 2maven
		<dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.8</version>
        </dependency>
        		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.2.2</version>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.0.1</version>
		</dependency>
		<dependency>
    		<groupId>commons-codec</groupId>
    		<artifactId>commons-codec</artifactId>
   			 <version>1.10</version>
		</dependency>
			<dependency>
	    <groupId>net.sf.json-lib</groupId>
	    <artifactId>json-lib</artifactId>
	    <version>2.4</version>
	    <classifier>jdk15</classifier>
	</dependency>
	<dependency>
    		<groupId>org.jdom</groupId>
    		<artifactId>jdom</artifactId>
    		<version>2.0.2</version>
	</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>4.4.10</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.5.2</version>
</dependency>
  <dependency>
    <groupId>commons-httpclient</groupId>
    <artifactId>commons-httpclient</artifactId>
    <version>3.1</version>
  </dependency>   
  • 3 controller
        
        //回调地址
        String notifyUrl = PropUtil.getString("notifyurl", "", Constant.H5PAY_FILE);
        if (notifyUrl.endsWith("/")){
            notifyUrl+="h5";
        }else{
            notifyUrl+="/h5";
        }
        
        String APPID = PropUtil.getString("h5payAPPID");//商户号取配置;
        String MERID = PropUtil.getString("h5payMERID");//;
        String SIGNKEY = "&^$&())^%3124";//;
        String h5payurl=PropUtil.getString("h5payurl");
        logger.info("APPID="+APPID);
		String spbill_create_ip = getIpAddr(request);//生产
//		System.out.println("spbill_create_ip="+spbill_create_ip);
		logger.info("spbill_create_ip="+spbill_create_ip);
		//String spbill_create_ip = "";//测试地址,也就是本地真是ip,用于本地测试用
		String scene_info = "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \""+h5payurl+"\",\"wap_name\": \"代理购钻\"}}";//我这里是网页入口,app入口参考文档的安卓和ios写法  授权地址 和购买信息
		String tradeType = "MWEB";//H5支付标记
		String MD5 = "MD5";//虽然官方文档不是必须参数,但是不送有时候会验签失败
		JSONObject result = new JSONObject();
		String subject = "代理购钻";//前端上送的支付主题
		String total_amount = String.valueOf(orderAmount);//前端上送的支付金额
		String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
		//金额转化为分为单位 微信支付以分为单位
		
		BigDecimal b1 = new BigDecimal(Double.valueOf(total_amount));   
		BigDecimal b2 = new BigDecimal(Double.toString(0.01));   
		logger.info("b1:"+b1.toString());
		logger.info("b2:"+b2.toString());
		BigDecimal b3= new BigDecimal(b1.multiply(b2).doubleValue()).setScale(0, BigDecimal.ROUND_HALF_UP);   
		
		logger.info(b3.toString());
		String finalmoney = StringUtil.getMoney(b3.toString());//;
		logger.info(finalmoney);
		int randomNum  = (int) (Math.random() * 1999+5000);
		/*String out_trade_no = Util.getSysTime("yyyyMMddHHmmss") + randomNum;*/
		//随机数 
		String nonce_str= MD5Utils.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
//		String nonce_str = RandomStringUtils.randomAlphabetic(10);
		//签名数据
		StringBuilder sb = new StringBuilder();
		sb.append("appid="+APPID);
		sb.append("&body="+subject);
		sb.append("&mch_id="+MERID);
		sb.append("&nonce_str="+nonce_str);
		sb.append("&notify_url="+notifyUrl);
		sb.append("&out_trade_no="+out_trade_no);
		sb.append("&scene_info="+scene_info);
		sb.append("&sign_type="+"MD5");
		sb.append("&spbill_create_ip="+spbill_create_ip);
		sb.append("&total_fee="+finalmoney);
		sb.append("&trade_type="+tradeType);
		sb.append("&key="+SIGNKEY);
		System.out.println("sb="+sb);
		logger.info("sb="+sb);
		//签名MD5加密
		String sign = MD5Utils.md5Encode(sb.toString()).toUpperCase();
		System.out.println("sign="+sign);
		logger.info("签名数据:"+sign);
		//封装xml报文
		String xml="<xml>"+
		        "<appid>"+ APPID+"</appid>"+
		        "<mch_id>"+ MERID+"</mch_id>"+
		        "<nonce_str>"+nonce_str+"</nonce_str>"+
		        "<sign>"+sign+"</sign>"+
		        "<body>"+subject+"</body>"+//
		        "<out_trade_no>"+out_trade_no+"</out_trade_no>"+
		        "<total_fee>"+finalmoney+"</total_fee>"+//
		        "<trade_type>"+tradeType+"</trade_type>"+
		        "<notify_url>"+notifyUrl+"</notify_url>"+
		        "<sign_type>MD5</sign_type>"+
		        "<scene_info>"+scene_info+"</scene_info>"+
		        "<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
		        "</xml>";
		
		String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信统一下单接口
		String mweb_url = "";
		Map map = new HashMap();
		logger.info("报文:"+xml);
		try {
	    	//预下单 获取接口地址
	    	map = WebUtils.getMwebUrl(createOrderURL, xml);
	    	logger.info("请求返回结果:"+map);
    		String return_code  = (String) map.get("return_code");
	        String return_msg = (String) map.get("return_msg");
	        if("SUCCESS".equals(return_code) && "OK".equals(return_msg)){
	        	 mweb_url = (String) map.get("mweb_url");//调微信支付接口地址
//	        	 System.out.println("mweb_url="+mweb_url);
	        	 
	        	 logger.info("create order url:"+mweb_url);
                    //操作自己的业务逻辑
	            
	             OutputUtil.output(MessageBuilder.newInstance()
	                             .builderCodeMessage(1000, "OK").builder("body","代理购钻").builder("pay_info",mweb_url).builder("type","微信支付")
	                             .builder("out_trade_no", orderInfo.getOrderId()).builder("total_fee", orderInfo.getOrderAmount())
	                     , request, response, null, false);
	             return;
	        	 
	        }else{
	        	 OutputUtil.output(1002, "购钻失败,请稍后再试", request, response, false);
	             return;
	        }
	    } catch (Exception e) {
	    	logger.error("Exception:" + e.getMessage(), e);
	    }
  • 4工具类
/**
 * Project Name:pay-protocol
 * File Name:Xml.java
 * Package Name:cn.swiftpass.pay.protocol
 * Date:2014-8-10下午10:48:21
 *
*/

package com.sy.util;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.*;

/**
 * ClassName:Xml
 * Function: XML的工具方法
 * Date:     2014-8-10 下午10:48:21 
 * @author    
 */
public class XmlUtils {
    
    /** <一句话功能简述>
     * <功能详细描述>request转字符串
     * @param request
     * @return
     * @see [类、类#方法、类#成员]
     */
    public static String parseRequst(HttpServletRequest request){
        String body = "";
        try {
            ServletInputStream inputStream = request.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            while(true){
                String info = br.readLine();
                if(info == null){
                    break;
                }
                if(body == null || "".equals(body)){
                    body = info;
                }else{
                    body += info;
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }            
        return body;
    }
    
    public static String parseXML(SortedMap<String, String> 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 (null != v && !"".equals(v) && !"appkey".equals(k)) {
                sb.append("<" + k + ">" + parameters.get(k) + "</" + k + ">\n");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 从request中获得参数Map,并返回可读的Map
     * 
     * @param request
     * @return
     */
    public static SortedMap getParameterMap(HttpServletRequest request) {
        // 参数Map
        Map properties = request.getParameterMap();
        // 返回值Map
        SortedMap returnMap = new TreeMap();
        Iterator entries = properties.entrySet().iterator();
        Map.Entry entry;
        String name = "";
        String value = "";
        while (entries.hasNext()) {
            entry = (Map.Entry) entries.next();
            name = (String) entry.getKey();
            Object valueObj = entry.getValue();
            if(null == valueObj){
                value = "";
            }else if(valueObj instanceof String[]){
                String[] values = (String[])valueObj;
                for(int i=0;i<values.length;i++){
                    value = values[i] + ",";
                }
                value = value.substring(0, value.length()-1);
            }else{
                value = valueObj.toString();
            }
            returnMap.put(name, value.trim());
        }
        return returnMap;
    }
    
    /**
     * 转XMLmap
     * @author  
     * @param xmlBytes
     * @param charset
     * @return
     * @throws Exception
     */
    public static Map<String, String> toMap(byte[] xmlBytes,String charset) throws Exception{
        SAXReader reader = new SAXReader(false);
        InputSource source = new InputSource(new ByteArrayInputStream(xmlBytes));
        source.setEncoding(charset);
        Document doc = reader.read(source);
        Map<String, String> params = XmlUtils.toMap(doc.getRootElement());
        return params;
    }
    
    /**
     * 转MAP
     * @author  
     * @param element
     * @return
     */
    public static Map<String, String> toMap(Element element){
        Map<String, String> rest = new HashMap<String, String>();
        List<Element> els = element.elements();
        for(Element el : els){
            rest.put(el.getName().toLowerCase(), el.getTextTrim());
        }
        return rest;
    }
    
    public static String toXml(Map<String, String> params){
        StringBuilder buf = new StringBuilder();
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        buf.append("<xml>");
        for(String key : keys){
            buf.append("<").append(key).append(">");
            buf.append("<![CDATA[").append(params.get(key)).append("]]>");
            buf.append("</").append(key).append(">\n");
        }
        buf.append("</xml>");
        return buf.toString();
    }
}

package com.sy.util;

import com.alibaba.fastjson.JSON;
import com.sy.mainland.util.HttpsUtil;
import com.sy.mainland.util.SHAUtil;

import com.alibaba.fastjson.JSON;
import com.sy.mainland.util.HttpsUtil;
import com.sy.mainland.util.SHAUtil;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.RequestEntity;

import java.io.BufferedInputStream;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class WebUtils {
	 
	public static Map getMwebUrl(String url,String xmlParam){
	  String jsonStr = null;
      HttpClient httpClient = new HttpClient();
      Map map = new HashMap();
      try {
    	 	PostMethod method = null;
			StringRequestEntity reqEntity = new StringRequestEntity(xmlParam,"text/json","UTF-8");
			method = new PostMethod(url);
			method.setRequestEntity(reqEntity);
			method.addRequestHeader("Content-Type","application/json;charset=utf-8");
			httpClient.executeMethod(method);
			StringBuffer resBodyBuf = new StringBuffer();
			byte[] responseBody = new byte[1024];
			int readCount = 0;
			BufferedInputStream is = new BufferedInputStream(method.getResponseBodyAsStream());
			while((readCount = is.read(responseBody,0,responseBody.length))!=-1){
				resBodyBuf.append(new String(responseBody,0,readCount,"utf-8"));
			}
			jsonStr = resBodyBuf.toString();
        System.out.println(jsonStr);
//        map = XmlUtils.parseXmlToList(jsonStr);
         map = XmlUtils.toMap(jsonStr.getBytes(), "utf-8");
    } catch (Exception e) {
        e.printStackTrace();
    }
    return map;
  }
}

package com.sy.util;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;

/**
 * 采用MD5加密
 * 
 * @author relax
 */
public class MD5Utils {

	public static String md5U(String inStr) {
		return md5Encode(inStr).toUpperCase();
	}

	/***
	 * MD5加密 生成32位md5码
	 * 
	 * @param 待加密字符串
	 * @return 返回32位md5码
	 */
	public static String md5Encode(String inStr) {
		MessageDigest md5 = null;
		try {
			md5 = MessageDigest.getInstance("MD5");
		} catch (Exception e) {
			System.out.println(e.toString());
			e.printStackTrace();
			return "";
		}

		byte[] byteArray;
		StringBuffer hexValue = new StringBuffer();
		try {
			byteArray = inStr.getBytes("UTF-8");
			byte[] md5Bytes = md5.digest(byteArray);

			for (int i = 0; i < md5Bytes.length; i++) {
				int val = ((int) md5Bytes[i]) & 0xff;
				if (val < 16) {
					hexValue.append("0");
				}
				hexValue.append(Integer.toHexString(val));
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		return hexValue.toString();
	}

	public static String md5For16U(String inStr) {
		return md5For16(inStr).toUpperCase();
	}

	public static String md5For16(String inStr) {
		MessageDigest md5 = null;
		try {
			md5 = MessageDigest.getInstance("MD5");
		} catch (Exception e) {
			System.out.println(e.toString());
			e.printStackTrace();
			return "";
		}

		byte[] byteArray;
		StringBuffer hexValue = new StringBuffer();
		try {
			byteArray = inStr.getBytes("UTF-8");
			byte[] md5Bytes = md5.digest(byteArray);

			for (int i = 0; i < md5Bytes.length; i++) {
				int val = ((int) md5Bytes[i]) & 0xff;
				if (val < 16) {
					hexValue.append("0");
				}
				hexValue.append(Integer.toHexString(val));
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		return hexValue.toString().substring(8, 24);
	}

	/**
	 * 测试主函数
	 * 
	 * @param args
	 * @throws Exception
	 */
	public static void main(String args[]) throws Exception {
		String str = new String("amigoxiexiexingxing");
		System.out.println("原始:" + str);
		System.out.println("MD5后:" + md5Encode(str));
	}

	public final static String getMessageDigest(byte[] buffer) {
        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
        try {
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(buffer);
            byte[] md = mdTemp.digest();
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            return null;
        }
    }


	
}
/**
     * 元转换成分
     * @param money
     * @return
     */
    public static String getMoney(String amount) {
        if(amount==null){
            return "";
        }
        // 金额转化为分为单位
        String currency =  amount.replaceAll("\\$|\\¥|\\,", "");  //处理包含, ¥ 或者$的金额  
        int index = currency.indexOf(".");  
        int length = currency.length();  
        Long amLong = 0l;  
        if(index == -1){  
            amLong = Long.valueOf(currency+"00");  
        }else if(length - index >= 3){  
            amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));  
        }else if(length - index == 2){  
            amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);  
        }else{  
            amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");  
        }  
        return amLong.toString(); 
    }
  • 5 回调
 @RequestMapping(value = "/notify/h5")
	public void weixinPayNotify(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		 String respString = "fail";
	try {
		BufferedReader reader = req.getReader();
        String line = "";
        Map map = new HashMap();
        String xml = "<xml><return_code><![CDATA[FAIL]]></xml>";;
        JSONObject dataInfo = new JSONObject();
        StringBuffer inputString = new StringBuffer();
        while ((line = reader.readLine()) != null) {
            inputString.append(line);
        }
        req.getReader().close();
//        System.out.println("----接收到的报文---"+inputString.toString());
        logger.info("----接收到的报文---"+inputString.toString());
        if(inputString.toString().length()>0){
//        	 map = XmlUtils.parseXmlToList(inputString.toString());
        	 map = XmlUtils.toMap(inputString.toString().getBytes(), "utf-8");
        }else{
        	System.out.println("接受微信报文为空");
        }
//        System.out.println("map="+map);
        logger.info("map="+map);
        if(map!=null && "SUCCESS".equals(map.get("result_code"))){
        	//成功的业务。。。
        	xml = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        }
	} catch (Exception e) {
		 logger.error("Exception:" + e.getMessage(), e);
	}finally {
        resp.getWriter().write(respString);
        resp.getWriter().flush();
        resp.getWriter().close();
    }
	}

注:伸手党仔细看一下

 支付常见错误

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值