注意:开发支付功能前,需要登录微信小程序后台开通微信支付功能

第一步:获取支付参数
获取支付时所需openId参考此篇博文js调用,获取参数

wx.login({
   success: res => {
     // 发送 res.code 到后台换取 openId, sessionKey, unionId
     wx.request({
       url: that.data.url + '/mini_Weixin_getOpenId.action',
       data:{"code":res.code},
       method:'GET',
       header: {
         'content-type': 'application/json'
       },
       success: function (res) {
           openId = res.data;
           wx.request({
             url: that.data.url + '/mini_WeixinPay_getPayParameter.action',
             data:{"openId":openId,"totalAmount":payData.payAmount,"orderId":payData.orderId},
             method:'GET',
             header: {
               'content-type': 'application/json'
             },
             success: function (res) {
               that.goPay(res);  //调用微信支付
             
             }
           })
       }
     })
   }
 })

后端代码

/**
	 * 小程序统一下单接口,并返回相关参数
	 */
public String getPayParameter(){
	
	try {
		String notify_url = "支付成功回调地址";
		reqIp = getIpAddress();
		JSONObject json = new JSONObject();	
		String totalFeeStr = "";//原始的支付金额,单位元
		Float totalFee = 0.0f;
		//随机数 
		//String nonce_str = "1add1a30ac87aa2db72f57a2375d8fec";
		String nonce_str = UUID.randomUUID().toString().replaceAll("-", "");
		//商品描述
		String body = "测试";
		//商户订单号
		String out_trade_no = orderId;
		//总金额
		Integer total_fee = 0;//转化成单位,元--分
		
		String time_start =DateUtil.formatDateTime();

		double totalAmount_all = 0;
		logger.info("/ns/toPay=reqIP==="+reqIp);
		totalAmount_all = Arith.add(totalAmount_all, Double.valueOf(totalAmount));
		totalFeeStr = Double.toString(totalAmount_all);
		totalFee = new Float(totalAmount_all);
		total_fee = Math.round(totalFee*100);//转化成单位,元--分
		logger.info("/ns/toPay=totalFeeStr==="+totalFeeStr);
		logger.info("/ns/toPay=totalFee==="+totalFee);
		logger.info("/ns/toPay=total_fee==="+total_fee);
		logger.info("/ns/toPay=body==="+body);
		logger.info("/ns/toPay=time_start==="+time_start);
					
		//获取统一下单需要的openid
		logger.info("weixToPay.openId:===@==" + openId);
		try {
			if(openId==null||"".equals(openId)){
				logger.info("获取统一下单需要的openid失败!");
				return json.toString();

			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

		//获取sign
		SortedMap<String, String> packageParams = new TreeMap<String, String>();
		packageParams.put("appid", "小程序appId");
		packageParams.put("mch_id","商户号");
		packageParams.put("nonce_str", nonce_str);
		packageParams.put("body", body);
		packageParams.put("out_trade_no", out_trade_no);
		packageParams.put("total_fee", total_fee+"");
		packageParams.put("spbill_create_ip", reqIp);
		packageParams.put("notify_url", notify_url);
		packageParams.put("trade_type", "JSAPI");  
		packageParams.put("openid", openId);  

		RequestHandler reqHandler = new RequestHandler(this.getRequest(), this.getResponse());
		reqHandler.init("小程序appId", "小程序appsecret", "商户密钥");
		
		String sign = reqHandler.createSign(packageParams);
		//System.out.println("sign:===@=="+sign);
		String xml="<xml>"+
				"<appid>"+小程序appId+"</appid>"+
				"<mch_id>"+商户号+"</mch_id>"+
				"<nonce_str>"+nonce_str+"</nonce_str>"+
				"<sign>"+sign+"</sign>"+
				"<body><![CDATA["+body+"]]></body>"+
				"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
				"<total_fee>"+total_fee+"</total_fee>"+
				"<spbill_create_ip>"+reqIp+"</spbill_create_ip>"+  //终端ip
				"<notify_url>"+notify_url+"</notify_url>"+
				"<trade_type>JSAPI</trade_type>"+
				"<openid>"+openId+"</openid>"+
				"</xml>";
		logger.info("xml:===@=="+xml);
		
		//获取openId后调用统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder
		//获取预支付prepay_id
		String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
		String prepay_id="";
		try {
			prepay_id = MiniUtil.getPayNo(createOrderURL, xml);
			logger.info("获取预支付prepay_id:===@==" + prepay_id);
			if(prepay_id.equals("")){
				json.put("orderId", orderId);
				json.put("orderName", body);
				json.put("totalFee", totalFeeStr);
				json.put("createTime", time_start);
				json.put("ErrorMsg", "统一支付接口获取预支付订单出错");
				return json.toString();
				
			}
		} catch (Exception e1) {
			 e1.printStackTrace();
			 return null;
		}
		SortedMap<String, String> finalpackage = new TreeMap<String, String>();
		String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
		String packages = "prepay_id="+prepay_id;
		finalpackage.put("appId", "小程序appId");
		finalpackage.put("timeStamp", timestamp);
		finalpackage.put("nonceStr", nonce_str);
		finalpackage.put("package", packages);
		finalpackage.put("signType", "MD5");
		String finalsign = reqHandler.createSign(finalpackage);
	

		json.put("appid", "小程序appId");
		json.put("timeStamp", timestamp);
		json.put("nonceStr", nonce_str);
		json.put("createTime", time_start);
		json.put("package", packages);
		json.put("paySign", finalsign);
		json.put("orderId", orderId);
		json.put("payPrice", total_fee);  //支付金额,单位分
		json.put("totalFeeStr", totalFeeStr); //原始的支付金额,单位元
		return json.toString();
		
	} catch (NumberFormatException e) {
		e.printStackTrace();
		logger.info(e);
		return null;
	}
	

}

运算方法

/**
* 提供精确的加法运算。
* @param v1 被加数
* @param v2 加数
* @return 两个参数的和
*/

public static double add(double v1,double v2){
    BigDecimal b1 = new BigDecimal(Double.toString(v1));
    BigDecimal b2 = new BigDecimal(Double.toString(v2));
    return b1.add(b2).doubleValue();
}

获取支付时所需Ip地址

public  String getIpAddress() {  
	HttpServletRequest request = this.getRequest();
       String ip = request.getHeader("x-forwarded-for");  
       if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
           ip = request.getHeader("Proxy-Client-IP");  
       }  
       if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
           ip = request.getHeader("WL-Proxy-Client-IP");  
       }  
       if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
           ip = request.getHeader("HTTP_CLIENT_IP");  
       }  
       if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
           ip = request.getHeader("HTTP_X_FORWARDED_FOR");  
       }  
       if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
           ip = request.getRemoteAddr();  
       }  
       return ip;  
 }

RequestHandler 工具类

public class RequestHandler {
/** Token获取网关地址地址 */
	private String tokenUrl;
	/** 预支付网关url地址 */
	private String gateUrl;
	/** 查询支付通知网关URL */
	private String notifyUrl;
	/** 商户参数 */
	private String appid;
	private String appkey;
	private String partnerkey;
	private String appsecret;
	private String key;
	/** 请求的参数 */
	private SortedMap parameters;
	/** Token */
	private String Token;
	private String charset;
	/** debug信息 */
	private String debugInfo;
	private String last_errcode;

	private HttpServletRequest request;

	private HttpServletResponse response;

	/**
	 * 初始构造函数。
	 * 
	 * @return
	 */
	public RequestHandler(HttpServletRequest request,
			HttpServletResponse response) {
		this.last_errcode = "0";
		this.request = request;
		this.response = response;
		//this.charset = "GBK";
		this.charset = "UTF-8";
		this.parameters = new TreeMap();
		// 验证notify支付订单网关
		notifyUrl = "https://gw.tenpay.com/gateway/simpleverifynotifyid.xml";
		
	}

	/**
	 * 初始化函数。
	 */
	public void init(String app_id, String app_secret,	String partner_key) {
		this.last_errcode = "0";
		this.Token = "token_";
		this.debugInfo = "";
		this.appid = app_id;
		this.partnerkey = partner_key;
		this.appsecret = app_secret;
		this.key = partner_key;
	}

	public void init() {
	}
	
	/**
	 * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
	 */
	public String createSign(SortedMap<String, String> packageParams) {
		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=" + this.getKey());
		String sign = MD5Util.MD5Encode(sb.toString(), this.charset)
				.toUpperCase();
		System.out.println("packge签名:" + sign);
		return sign;

	}
}

签名工具类

public class MD5Util {

	 private static String byteArrayToHexString(byte b[]) {  
	        StringBuffer resultSb = new StringBuffer();  
	        for (int i = 0; i < b.length; i++)  
	            resultSb.append(byteToHexString(b[i]));  
	  
	        return resultSb.toString();  
	    }  
	  
	    private static String byteToHexString(byte b) {  
	        int n = b;  
	        if (n < 0)  
	            n += 256;  
	        int d1 = n / 16;  
	        int d2 = n % 16;  
	        return hexDigits[d1] + hexDigits[d2];  
	    }  
	  
	    public static String MD5Encode(String origin, String charsetname) {  
	        String resultString = null;  
	        try {  
	            resultString = new String(origin);  
	            MessageDigest md = MessageDigest.getInstance("MD5");  
	            if (charsetname == null || "".equals(charsetname))  
	                resultString = byteArrayToHexString(md.digest(resultString  
	                        .getBytes()));  
	            else  
	                resultString = byteArrayToHexString(md.digest(resultString  
	                        .getBytes(charsetname)));  
	        } catch (Exception exception) {  
	        }  
	        return resultString;  
	    }  
	  
	    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",  
	            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };  
	  
}

获取预支付订单工具类

public class MiniUtil {

	/**
	 * 模拟浏览器post提交
	 * 
	 * @param url
	 * @return
	 */
	public static HttpPost getPostMethod(String url) {
		HttpPost pmethod = new HttpPost(url); // 设置响应头信息
		pmethod.addHeader("Connection", "keep-alive");
		pmethod.addHeader("Accept", "*/*");
		pmethod.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
		pmethod.addHeader("Host", "api.mch.weixin.qq.com");
		pmethod.addHeader("X-Requested-With", "XMLHttpRequest");
		pmethod.addHeader("Cache-Control", "max-age=0");
		pmethod.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
		return pmethod;
	}

	/**
	 * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
	 * @param strxml
	 * @throws JDOMException
	 * @throws IOException
	 * @return Map
	 */
	public static Map doXMLParse(String strxml) throws Exception {
		if (null == strxml || "".equals(strxml)) {
			return null;
		}
		Map m = new HashMap();
		InputStream in = new ByteArrayInputStream(strxml.getBytes());
		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;
	}
	/**
	 * 获取子结点的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(getChildrenText(list));
				}
				sb.append(value);
				sb.append("</" + name + ">");
			}
		}

		return sb.toString();
	}
	
	//获取预支付订单
	public static String getPayNo(String url, String xmlParam) {
		DefaultHttpClient client = new DefaultHttpClient();
		client.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,true);
		HttpPost httpost = getPostMethod(url);
		String prepay_id = "";
		try {
			try {
				httpost.setEntity(new StringEntity(xmlParam, "UTF-8"));
				HttpResponse response = client.execute(httpost);
				
				String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
				Map<String, Object> dataMap = new HashMap<String, Object>();
				logger.info("预支付下单结果:====@==="+jsonStr);
				if (jsonStr.indexOf("FAIL") != -1) {
					return prepay_id;
				}
				Map map = doXMLParse(jsonStr);
				String return_code = (String) map.get("return_code");
				prepay_id = (String) map.get("prepay_id");
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(!httpost.isAborted()){
				httpost.abort();
			}
			client.getConnectionManager().shutdown();
		}
		return prepay_id;
	}
}

第二步:小程序调起微信支付,完成支付

//微信支付
goPay(payData) {
	 wx.requestPayment({
	    timeStamp: payData.data.timeStamp,
	    nonceStr: payData.data.nonceStr,
	    package: payData.data.package,
	    signType: 'MD5',
	    paySign: payData.data.paySign,
	    success(res) {
	      console.log("支付成功", res)
	      //你可以在这里支付成功以后,再跳会webview页,并把支付成功状态传回去
	      wx.reLaunch({
	        url: '/pages/paySuccess/paySuccess',
	      });
	    },
	    fail(res) {
	      console.log("支付失败", res);
	      wx.reLaunch({
	        url: '/pages/payFail/payFail',
	      });
	    }
	})
}