Java--微信小程序支付小结

pom依赖:

<dependency>
			<groupId>dom4j</groupId>
			<artifactId>dom4j</artifactId>
			<version>1.6.1</version>
</dependency>

微信小程序js:

 wx.request({
	  //url:微信小程序js获取价格后将价格和openId传入后台路径
      url: '*********',
      method: 'post',
      header: { 'conten-type': 'application/x-www-from-urlencoded' },
      success: function (res) {
          var payEncodeStr = res.data;
          //调起支付API
          wx.requestPayment({
            'timeStamp': payEncodeStr.timeStamp,
            'nonceStr': payEncodeStr.nonceStr,
            'package': payEncodeStr.package,
            'signType': payEncodeStr.signType,
            'paySign': payEncodeStr.paySign,
            'success': function (res) {
			  //起调成功后操作
			  *****************
            },
            'fail': function (res) {
            },
            'complete': function (res) {
            }
          })
        }
      },
      fail: function (res) {
      }
})

java后台:
微信小程序发起支付需要的常量:

public interface Constants {
	//小程序APPID
    public static final String APPID = "*********";
    //微信支付的商户id
    public static final String MCH_ID = "*********";
    //微信支付的商户密钥
    public static final String PARTNERKEY = "**********";
    //支付成功后的服务器回调url
    public static final String NOTIFY_URL = "***********";
    //签名方式
    public static final String SIGNTYPE = "MD5";
    //交易类型
    public static final String TRADETYPE = "JSAPI";
    //微信统一下单接口地址
    public static final String PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
}

业务代码:

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

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

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.util.DigestUtils;


public class ProgramPayMent {

	/**
	 * 支付起调(小程序调用js后进入此方法)
	 */
	public Map<String, String> programMent(String openId, String price) {
		// 返回给移动端需要的参数
		Map<String, String> response = new HashMap<String, String>();
		try {
			// 商户订单号
			String transactionId = "根据自己的生成规则生成";
			// 生成的随机字符串
			String nonce_str = getRandomString(32);
			// 商品名称
			String body = "********";
			// 本机的ip地址
			String spbill_create_ip = getLocalIp();
			String money = getMoney(price);// 支付金额,单位:分,这边需要转成字符串类型,否则后面的签名会失败
			Map<String, String> packageParams = new HashMap<String, String>();
			// 小程序ID,微信分配的小程序ID
			packageParams.put("appid", Constants.APPID);
			// 商户号,微信支付分配的商户号
			packageParams.put("mch_id", Constants.MCH_ID);
			// 随机字符串,长度要求在32位以内。
			packageParams.put("nonce_str", nonce_str);
			// 商品简单描述
			packageParams.put("body", body);
			// 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
			packageParams.put("out_trade_no", transactionId);// 商户订单号
			// 订单总金额,单位为分
			packageParams.put("total_fee", money);
			// 终端IP,支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP
			packageParams.put("spbill_create_ip", spbill_create_ip);
			// 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
			packageParams.put("notify_url", Constants.NOTIFY_URL);
			// 交易类型(JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付,不同trade_type决定了调起支付的方式,请根据支付产品正确上传)
			packageParams.put("trade_type", Constants.TRADETYPE);
			// 用户标识,trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
			packageParams.put("openid", openId);
			// 除去数组中的空值和签名参数
			packageParams = paraFilter(packageParams);
			String prestr = createLinkString(packageParams); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
			// MD5运算生成签名,这里是第一次签名,用于调用统一下单接口,key为商户平台设置的密钥key
			String mysign = sign(prestr, Constants.PARTNERKEY, "utf-8").toUpperCase();
			// 拼接统一下单接口使用的xml数据,要将上一步生成的签名一起拼接进去
			String xml = "<xml version='1.0' encoding='gbk'>" + "<appid>" + Constants.APPID + "</appid>"
					+ "<body><![CDATA[" + body + "]]></body>" + "<mch_id>" + Constants.MCH_ID + "</mch_id>"
					+ "<nonce_str>" + nonce_str + "</nonce_str>" + "<notify_url>" + Constants.NOTIFY_URL
					+ "</notify_url>" + "<openid>" + openId + "</openid>" + "<out_trade_no>" + transactionId
					+ "</out_trade_no>" + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"
					+ "<total_fee>" + money + "</total_fee>" + "<trade_type>" + Constants.TRADETYPE + "</trade_type>"
					+ "<sign>" + mysign + "</sign>" + "</xml>";
			System.out.println(xml);
			// 调用统一下单接口,并接受返回的结果
			String result = httpRequest(Constants.PAY_URL, "POST", xml);
			// 解析结果,将结果存在map中
			Map map = doXMLToMap(result);
			String return_code = (String) map.get("return_code");

			if (return_code == "SUCCESS" || return_code.equals(return_code)) {
				// 业务结果
				String prepay_id = (String) map.get("prepay_id");// 返回的预付单信息
				response.put("nonceStr", nonce_str);
				response.put("package", "prepay_id=" + prepay_id);
				Long timeStamp = System.currentTimeMillis() / 1000;
				response.put("timeStamp", timeStamp + "");// 这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误

				String stringSignTemp = "appId=" + Constants.APPID + "&nonceStr=" + nonce_str + "&package=prepay_id="
						+ prepay_id + "&signType=" + Constants.SIGNTYPE + "&timeStamp=" + timeStamp;
				// 再次签名,这个签名用于小程序端调用wx.requesetPayment方法
				String paySign = sign(stringSignTemp, Constants.PARTNERKEY, "utf-8").toUpperCase();

				response.put("paySign", paySign);
				response.put("appid", Constants.APPID);

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

	/**
	 * 支付回调(常量中 NOTIFY_URL 的值为该路径)
	 */
	public void callback(HttpServletRequest request, HttpServletResponse response) {
		try {
			InputStream inputStream = request.getInputStream();
			// 获取请求输入流
			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = inputStream.read(buffer)) != -1) {
				outputStream.write(buffer, 0, len);
			}
			outputStream.close();
			inputStream.close();
			// 回调数据
			Map<String, String> map = getMapFromXML(new String(outputStream.toByteArray(), "utf-8"));
			String resXml = "";
			String returnCode = map.get("return_code");
			if ("SUCCESS".equalsIgnoreCase(returnCode)) {
				String returnmsg = map.get("result_code");
				if ("SUCCESS".equals(returnmsg)) {
					/**
					 *
					 * 业务代码
					 *
					 */
					// 支付成功
					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);
				}
			} else {
				resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
						+ "<return_msg><![CDATA[报文为空]></return_msg>" + "</xml>";
				System.out.println("【订单支付失败】");
			}
			System.out.println("响应内容:" + resXml);
			response.getWriter().print(resXml);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 生成随机字符串
	 */
	public static String getRandomString(int length) {
		String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		Random random = new Random();
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < length; i++) {
			int number = random.nextInt(62);
			sb.append(str.charAt(number));
		}
		return sb.toString();
	}

	/**
	 * 获取本机IP
	 * 
	 * @return
	 */
	public static String getLocalIp() {
		InetAddress ia = null;
		String localip = null;
		try {
			ia = ia.getLocalHost();
			localip = ia.getHostAddress();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return localip;
	}

	/**
	 * 将金额转换为分
	 */
	public static String getMoney(String mon) {
		String finalmoney = "";
		Double db_money = Double.parseDouble(mon.toString().replace(" ", ""));
		String result;
		double d2 = 100.0;
		BigDecimal bd1 = new BigDecimal(Double.toString(db_money));
		BigDecimal bd2 = new BigDecimal(Double.toString(d2));
		result = bd1.multiply(bd2).doubleValue() + ""; // 乘
		finalmoney = result.substring(0, result.indexOf("."));
		return finalmoney;
	}

	/**
	 * 除去数组中的空值和签名参数
	 * 
	 * @param sArray
	 *            签名参数组
	 * @return 去掉空值与签名参数后的新签名参数组
	 */
	public 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 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;
	}

	/**
	 * sign
	 */
	public String sign(String content, String key, String input_charset) {
		content = content + "&key=" + key;
		byte[] str = {};
		if (input_charset == null || "".equals(input_charset)) {
			str = content.getBytes();
		}
		try {
			str = content.getBytes(input_charset);
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + input_charset);
		}
		return DigestUtils.md5DigestAsHex(str);
	}

	/**
	 * httpRequest请求
	 */
	public 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);
			}
			br.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return buffer.toString();
	}

	/**
	 * 解析xml,取出其中的result_code,和prepay_id
	 */
	public HashMap<String, String> doXMLToMap(String results) {
		HashMap<String, String> result = new HashMap<String, String>();
		String return_code = results.substring(results.indexOf("<return_code><![CDATA[") + 22,
				results.indexOf("]]></return_code>"));
		result.put("return_code", return_code);
		if (return_code.equals("SUCCESS")) {
			String prepay_id = results.substring(results.indexOf("<prepay_id><![CDATA[") + 20,
					results.indexOf("]]></prepay_id>"));
			result.put("prepay_id", prepay_id);
		}
		return result;
	}

	/**
	 * 将xml字符串转换成map
	 */
	public static Map<String, String> getMapFromXML(String strXML) throws Exception {
		Document doc = DocumentHelper.parseText(strXML);
		Map<String, String> map = new HashMap<String, String>();
		if (doc == null) {
			return map;
		}
		Element root = doc.getRootElement();
		for (Iterator iterator = root.elementIterator(); iterator.hasNext();) {
			Element e = (Element) iterator.next();
			List list = e.elements();
			map.put(e.getName(), e.getText());
		}
		return map;
	}
}

我把所有业务代码放一起的可以根据自己需求分层~~~~~~~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值