微信小程序支付

首要条件:
    1.开通了微信支付,并且小程序绑定了微信支付;

    2.准备好小程序的appid,微信支付的商户号,支付秘钥。

微信小程序支付核心: 

    1、小程序向服务端发送获取openid

    2、服务端向微信统一下单

    3、服务器收到返回信息二次签名发回给小程序

    4、小程序发起支付

    5、服务端收到回调


说明:下面是小程序代码

<!--index.wxml-->
<view class="container">
   <button bindtap="payoff">发起支付</button>
</view>
//index.js
//获取应用实例
var app = getApp()
Page({
  data: {
    motto: 'Hello World',
    userInfo: {}
  },
  onLoad: function () {
    console.log('onLoad')
    
  },
  //发起支付
  payoff: function(e){
    var that = this;
    wx.login({
      success: function(res) {
        console.log('用户code ' + res.code)
        that.getOpenId(res.code);
      }
    });
    
  },
  //第一步:获取openid
  getOpenId: function(code){
    var that = this;
    wx.request({ 
      url: 'http://localhost:8080/game/login.do', 
        method: 'POST',
        header: {
           'content-type': 'application/x-www-form-urlencoded'
        },
        data: {'code':code},
        success: function(res) {
           var openId = res.data.data;
           console.log('用户openId '+openId)
          // that.xiadan(openId);
           that.sign(openId);      
        }
    })
  },

  //第二、三步
  sign: function (openId){
    var that = this;
    wx.request({
      url: 'http://localhost:8080/game/weixin/pay.do', 
        method: 'POST',
        header: {
           'content-type': 'application/x-www-form-urlencoded'
        },
        data: { 'openId': openId},
        success: function(res) {
           that.requestPayment(res.data.data);
        }
    })
  },
  //第四步:
  requestPayment: function(obj){
    wx.requestPayment({
      'timeStamp': obj.timeStamp,
      'nonceStr': obj.nonceStr,
      'package': obj.package,
      'signType': obj.signType,
      'paySign': obj.paySign,
      'success':function(res){
        
      },
      'fail':function(res){
      }
    })
  }  
})

说明:下面是Java代码

//第一步:通过code-->openid
        @ResponseBody
	@RequestMapping(value = "/login.do",method=RequestMethod.POST)
	public JsonResult listVote(String code,User user1, HttpServletRequest request) {
		String CODE = code;
		// 替换字符串,获得请求URL
		String token = UserInfoUtil.getWebAccess(CODE);// 通过自定义工具类组合出小程序需要的登录凭证
		// 通过https方式请求获得web_access_token并获得小程序的返回
		String response = HttpsUtil.httpsRequestToString(token, "GET", null);
		// 通过JsonObject解析小程序返回数据           
		JSONObject jsonObject = JSON.parseObject(response);
		System.out.println(jsonObject);
		// 如果JasonObject或opeid为空则登录失败
		String openid = jsonObject.getString("openid");
		return new JsonResult(openid);
	}
// 第二、三步:
	/** @Description: 发起微信支付
	 * @param request
	 */
	@RequestMapping(value = "/weixin/pay.do",method =RequestMethod.POST)
	@ResponseBody
	public JsonResult WeChatOrder(HttpServletRequest request,String openId) {
		Map<String, Object> response = new HashMap<>();
		try {
			String  openid = openId;
			// 生成的随机字符串
			String nonce_str = StringUtils.getRandomStringByLength(32);
			// 商品名称
			String body = "**-充值";
			// 获取客户端的ip地址
			//String spbill_create_ip = IpUtil.getIpAddr(request);
			String spbill_create_ip = "******";
			String out_trade_no = StringUtils.getRandomStringByLength(32);
			//String total_fee = (String) request.getAttribute("total_fee") * 100;
			String total_fee = "1";//这里室0.01元
			// 组装参数,用户生成统一下单接口的签名
			Map<String, String> packageParams = new HashMap<String, String>();
			packageParams.put("appid", UserInfoUtil.APPID);
			packageParams.put("mch_id", UserInfoUtil.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", spbill_create_ip);
			packageParams.put("notify_url", UserInfoUtil.NOTIFY_URL);// 支付成功后的回调地址
			packageParams.put("trade_type", UserInfoUtil.TRADETYPE);// 支付方式
			//packageParams.put("openid", user.getOpenId());
			packageParams.put("openid", openid);
			packageParams.put("sign_type", UserInfoUtil.SIGNTYPE);
			// 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
			String prestr = PayUtil.createLinkString(packageParams);
			System.out.println(prestr);
			// MD5运算生成签名,这里是第一次签名,用于调用统一下单接口 拼接API密钥
			String mysign = PayUtil.sign(prestr, UserInfoUtil.KEY, "utf-8").toUpperCase();
			System.out.println(mysign);
			// 拼接统一下单接口使用的xml数据,要将上一步生成的签名一起拼接进去
			String xml = "<xml>" 
								+"<appid>" + UserInfoUtil.APPID + "</appid>" 
								+ "<body><![CDATA[" + body+ "]]></body>" 
								+ "<mch_id>" + UserInfoUtil.MCH_ID + "</mch_id>" 
								+ "<nonce_str>" + nonce_str+ "</nonce_str>"
								+ "<notify_url>" + UserInfoUtil.NOTIFY_URL + "</notify_url>" 
								+ "<openid>"+ openid + "</openid>" 
								+ "<out_trade_no>" + out_trade_no + "</out_trade_no>"
								+ "<sign_type>" +  UserInfoUtil.SIGNTYPE + "</sign_type>"
								+ "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>" 
								+ "<total_fee>" + total_fee + "</total_fee>" 
								+ "<trade_type>" + UserInfoUtil.TRADETYPE + "</trade_type>" 
								+ "<sign>" + mysign+ "</sign>" 
						+ "</xml>";
			System.out.println("调试模式_统一下单接口 请求XML数据:" + xml);
			// 调用统一下单接口,并接受返回的结果
			String result = PayUtil.httpRequest(UserInfoUtil.PAY_URL, "POST", xml);
			System.out.println("调试模式_统一下单接口 返回XML数据:" + result);
			// 将解析结果存储在HashMap中
			@SuppressWarnings("unchecked")
			Map<String, String> map = PayUtil.doXMLParse(result);
			// 返回信息
			String return_code = (String) map.get("return_code");// 返回状态码
			String return_msg = (String) map.get("return_msg");// 返回信息
			System.out.println(return_msg);
			// 返回给小程序端需要的参数
			response = new HashMap<String, Object>();
			if (return_code == "SUCCESS" || return_code.equals(return_code)) {
			//if (return_code == "SUCCESS") {
				// 返回的预付单信息
				String prepay_id = (String) map.get("prepay_id");
				response.put("nonceStr", nonce_str);
				response.put("signType", "MD5");
				response.put("package", "prepay_id=" + prepay_id);
				Long timeStamp = System.currentTimeMillis() / 1000;
				// 这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
				response.put("timeStamp", timeStamp + "");
				// 拼接签名需要的参数
				String stringSignTemp = "appId=" + UserInfoUtil.APPID + "&nonceStr=" + nonce_str + "&package=prepay_id="
						+ prepay_id + "&signType=MD5&timeStamp=" + timeStamp;
				// 再次签名,这个签名用于小程序端调用wx.requesetPayment方法
				String paySign = PayUtil.sign(stringSignTemp, UserInfoUtil.KEY, "utf-8").toUpperCase();
				response.put("paySign", paySign);
			}
			response.put("appid", UserInfoUtil.APPID);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new JsonResult(response);
	}
// 第五步:支付成功后回调接口
	/** @Description: 最终确认微信支付
	 * @return
	 * @throws Exception
	 */
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@RequestMapping(value = "/wxNotify.do")
	@ResponseBody
	public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// User user = (User) request.getSession().getAttribute("user");
//		User user = new User();
//		user.setUserId(1);
//		// 额外的金币
//		int extraGoldCoin = (int) request.getAttribute("extraGoldCoin");
		
		BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));
		String line = null;
		StringBuilder sb = new StringBuilder();
		while ((line = br.readLine()) != null) {
			sb.append(line);
		}
		// sb为微信返回的xml
		String notityXml = sb.toString();
		String resXml = "";
		System.out.println("接收到的报文:" + notityXml);
		Map map = PayUtil.doXMLParse(notityXml);
		String returnCode = (String) map.get("return_code");
		if ("SUCCESS".equals(returnCode)) {
			// 验证签名是否正确
			if (PayUtil.verify(PayUtil.createLinkString(map), (String) map.get("sign"), UserInfoUtil.KEY, "utf-8")) {
				/** 此处添加自己的业务逻辑代码start **/
	
				/** 此处添加自己的业务逻辑代码end **/
				// 通知微信服务器已经支付成功
				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> ";
		}
		System.out.println(resXml);
		System.out.println("微信支付回调数据结束");
		BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
		out.write(resXml.getBytes());
		out.flush();
		out.close();
	}
//所涉及的工具类等
package com.answer.utils;

public class UserInfoUtil {
	/**
	 * 这里填小程序的APPID
	 */
	public static final String APPID = "*****";
	/**
	 * 这里填该小程序的SECRET
	 */
	private static final String SECRET = "**************";
	/**
	 * 微信支付的商户id
	 */
	public static final String MCH_ID = "*******************";
	/**
	 * 微信支付的商户密钥
	 */
	public static final String KEY = "*********************";
	/**
	 * 支付成功后的服务器回调url
	 */
	public static final String NOTIFY_URL = "https://localhost:8080/wxNotify";
	/**
	 * 签名方式,固定值
	 */
	public static final String SIGNTYPE = "MD5";
	/**
	 * 交易类型,小程序支付的固定值为JSAPI
	 */
	public static final String TRADETYPE = "JSAPI";
	/**
	 * 微信统一下单接口地址
	 */
	public static final String PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

	// 获取code的请求地址
	public static String Get_Code = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=STAT#wechat_redirect";

	// 替换字符串
	public static String getCode(String APPID, String REDIRECT_URI, String SCOPE) {
		return String.format(Get_Code, APPID, REDIRECT_URI, SCOPE);
	}

	// 获取Web_access_token https的请求地址
	public static String Web_access_tokenhttps = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
/**
 * 以https方式发送请求的工具类
 */
public class HttpsUtil {
	/**
	 * 以https方式发送请求并将请求响应内容以String方式返回
	 * 
	 * @param path
	 *            请求路径
	 * @param method
	 *            请求方法
	 * @param body
	 *            请求数据体
	 * @return 请求响应内容转换成字符串信息
	 */
	public static String httpsRequestToString(String path, String method, String body) {
		if (path == null || method == null) {
			return null;
		}
		String response = null;
		InputStream inputStream = null;
		InputStreamReader inputStreamReader = null;
		BufferedReader bufferedReader = null;
		HttpsURLConnection conn = null;
		try {
			// 创建SSLConrext对象,并使用我们指定的信任管理器初始化
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			TrustManager[] tm = { new X509TrustManager() {
				public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				}
				@Override
				public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				}
				@Override
				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
			} };
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上面对象中得到SSLSocketFactory
			SSLSocketFactory ssf = sslContext.getSocketFactory();
			System.out.println("请求网址:" + path);
			URL url = new URL(path);
			conn = (HttpsURLConnection) url.openConnection();
			conn.setSSLSocketFactory(ssf);
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setUseCaches(false);
			// 设置请求方式(get|post)
			conn.setRequestMethod(method);
			// 有数据提交时
			if (null != body) {
				OutputStream outputStream = conn.getOutputStream();
				outputStream.write(body.getBytes("UTF-8"));
				outputStream.close();
			}
			// 将返回的输入流转换成字符串
			inputStream = conn.getInputStream();
			inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
			bufferedReader = new BufferedReader(inputStreamReader);
			String str = null;
			StringBuffer buffer = new StringBuffer();
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			response = buffer.toString();
		} catch (Exception e) {

		} finally {
			if (conn != null) {
				conn.disconnect();
			}
			try {
				bufferedReader.close();
				inputStreamReader.close();
				inputStream.close();
			} catch (IOException execption) {

			}
		}
		return response;
	}
}
public class StringUtils {
	/** 
     * StringUtils工具类方法 
     * 获取一定长度的随机字符串,范围0-9,a-z 
     * @param length:指定字符串长度 
     * @return 一定长度的随机字符串 
     */  
    public static String getRandomStringByLength(int length) {  
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";  
        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();  
       }  
}
public class PayUtil {
	/**
	 * 签名字符串
	 * 
	 * @param text需要签名的字符串
	 * @param key
	 *            密钥
	 * @param input_charset编码格式
	 * @return 签名结果
	 */
	public static String sign(String text, String key, String input_charset) {
		text = text + "&key=" + key;
		return DigestUtils.md5Hex(getContentBytes(text, input_charset));
	}

	/**
	 * 签名字符串
	 * 
	 * @param text需要签名的字符串
	 * @param sign
	 *            签名结果
	 * @param key密钥
	 * @param input_charset
	 *            编码格式
	 * @return 签名结果
	 */
	public static boolean verify(String text, String sign, String key, String input_charset) {
		// text = text + key;
		text = text + "&key=" + key;
		String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
		if (mysign.equals(sign)) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * @param content
	 * @param charset
	 * @return
	 * @throws SignatureException
	 * @throws UnsupportedEncodingException
	 */
	public static byte[] getContentBytes(String content, String charset) {
		if (charset == null || "".equals(charset)) {
			return content.getBytes();
		}
		try {
			return content.getBytes(charset);
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
		}
	}

	@SuppressWarnings("unused")
	private static boolean isValidChar(char ch) {
		if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
			return true;
		if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))
			return true;// 简体中文汉字编码
		return false;
	}

	/**
	 * 除去数组中的空值和签名参数
	 * 
	 * @param sArray
	 *            签名参数组
	 * @return 去掉空值与签名参数后的新签名参数组
	 */
	public static Map<String, String> paraFilter(Map<String, String> sArray) {
		Map<String, String> result = new HashMap<String, String>();
		if (sArray == null || sArray.size() <= 0) {
			return result;
		}
		for (String key : sArray.keySet()) {
			String value = sArray.get(key);
			if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
					|| key.equalsIgnoreCase("sign_type")) {
				continue;
			}
			result.put(key, value);
		}
		return result;
	}

	/**
	 * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
	 * 
	 * @param params
	 *            需要排序并参与字符拼接的参数组
	 * @return 拼接后字符串
	 */
	public static String createLinkString(Map<String, String> params) {
		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);
		String prestr = "";
		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);
			if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
				prestr = prestr + key + "=" + value;
			} else {
				prestr = prestr + key + "=" + value + "&";
			}
		}
		return prestr;
	}

	/**
	 * 
	 * @param requestUrl请求地址
	 * @param requestMethod请求方法
	 * @param outputStr参数
	 */
	public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
		// 创建SSLContext
		StringBuffer buffer = null;
		try {
			URL url = new URL(requestUrl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod(requestMethod);
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.connect();
			// 往服务器端写内容
			if (null != outputStr) {
				OutputStream os = conn.getOutputStream();
				os.write(outputStr.getBytes("utf-8"));
				os.close();
			}
			// 读取服务器端返回的内容
			InputStream is = conn.getInputStream();
			InputStreamReader isr = new InputStreamReader(is, "utf-8");
			BufferedReader br = new BufferedReader(isr);
			buffer = new StringBuffer();
			String line = null;
			while ((line = br.readLine()) != null) {
				buffer.append(line);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return buffer.toString();
	}

	public static String urlEncodeUTF8(String source) {
		String result = source;
		try {
			result = java.net.URLEncoder.encode(source, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return result;
	}

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


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值