前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java-小程序微信支付

java-小程序微信支付

作者头像
猿码优创
发布2019-07-28 13:57:29
3K3
发布2019-07-28 13:57:29
举报
文章被收录于专栏:猿码优创猿码优创

- -微信支付之小程序- -

哈喽 我是你们的KingYiFan,一直说把微信支付给分享出来一直没有机会。终于闲下来了。听着音乐给你们分享一下。不懂可以随时联系我。。


-~~ 本文介绍小程序微信支付的java服务端~~。

  • 微信小程序支付文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1
  • 编写之前请先浏览文档中的业务流程、API列表中的统一下单和支付结果通知。
  • 调用需要用到小程序的APPID、商户号、API密钥。请参考微信支付文档申请和获取。

首先给大家看一下微信支付的流程
废话不多说直接上微信官方流程图 哈哈哈哈哈 是不是看的一脸懵逼。
微信支付-小程序
微信支付-小程序

--KingYiFan 给大家准备了一个自己画的流程图

微信支付流程
微信支付流程
代码语言:javascript
复制
 /**
	     * @Title: 小程序微信支付
	     * @Description: 调用微信的支付接口 统一下单
	     * @author: KingYiFan
	     */
	      @ResponseBody
	      @RequestMapping(value = "wxpayTest", method = RequestMethod.GET, produces ="application/json; charset=utf-8")
	      public void wxpayTest(HttpServletRequest request, HttpServletResponse response, Model model) {
	    	try {
			//前台传输一个openid--每个用户对应小程序都会生成一个独一无二的openid
		    String openid = request.getParameter("openid");
			//创建一个实体类
			OrderInfo order = new OrderInfo();
			//appid 是小程序的appid(微信公众平台获取)
			order.setAppid(Configure.getAppID());
			//商户id(微信商户平台申请)
			order.setMch_id(Configure.getMch_id());
			//随机字符串,微信推荐长度要求在32位以内。
			order.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
			//商品描述-可以随便写
			order.setBody("KingYiFan-测试");
			//商户订单号可以根据这个订单查询账单
			order.setOut_trade_no(RandomStringGenerator.getRandomStringByLength(32));
			//支付金额 微信支付是按分为单位的 是int类型 1就是1分 10就是一角
			//这是一个坑
			order.setTotal_fee(1);
			//APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
			//微信写的一团懵逼 到底是用户的ip还是我们服务器的ip
			//算了 不管了就直接用我们服务器ip把
			order.setSpbill_create_ip(Configure.getIp());
			//微信支付回调地址 也就是用户支付成功改状态的地方
			order.setNotify_url("https://wxxcx.jiajiao2o.com/v2/wxPayNotify");
			//如果是微信小小程序和公众号这是一个固定值
			order.setTrade_type("JSAPI");
			//openid 我上面介绍过,这里不解释了。。。
			order.setOpenid(openid);
			//封装签名的格式 md5 
			order.setSign_type("MD5");
			// 生成签名 一会我会把生成签名格式甩到下面
			String sign = Signature.getSign(order);
			//封装签名
			order.setSign(sign);
			//用httpclient 发送一个post请求微信官方Api
			String result = HttpRequest.sendPost("https://api.mch.weixin.qq.com/pay/unifiedorder", order);
			//返回一个预订单id
			System.out.println(result);
			//来回传输数据是xml格式的,然后返回结果也是xml格式的 解析xml
			XStream xStream = new XStream();
			xStream.alias("xml", OrderReturnInfo.class);
			//获得微信返回的结果
			OrderReturnInfo returnInfo = (OrderReturnInfo) xStream.fromXML(result);

			// 进行二次签名
			SignInfo signInfo = new SignInfo();
			//appid 是小程序appid 上面有介绍
			signInfo.setAppId(Configure.getAppID());
			//当前时间戳
			long time = System.currentTimeMillis() / 1000;
			signInfo.setTimeStamp(String.valueOf(time));
			//随机字符串 建议32位
			signInfo.setNonceStr(RandomStringGenerator.getRandomStringByLength(32));
			//预订单ip 上一次请求返回的id
			signInfo.setRepay_id("prepay_id=" + returnInfo.getPrepay_id());
			//签名加密方式
			signInfo.setSignType("MD5");
			// 封装参数返回小程序段
			String sign2 = Signature.getSign(signInfo);
			JSONObject json = new JSONObject();
			json.put("timeStamp", signInfo.getTimeStamp());
			json.put("nonceStr", signInfo.getNonceStr());
			json.put("package", signInfo.getRepay_id());
			json.put("signType", signInfo.getSignType());
			json.put("paySign", sign2);
			response.getWriter().append(json.toJSONString());
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
	}

上面的代码只是调起来微信支付,下面我再说一下用户支付成功回调

废话不多说直接上代码:
代码语言:javascript
复制
/**
	  * @Description: 提交支付后的微信异步返回接口
	  * @author: KingYiFan
	  */
	 @ResponseBody
     @RequestMapping(value = "wxPayNotify", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
	public void wxPayNotify(HttpServletRequest request, HttpServletResponse response) {

		String resXml = "";
		Map<String, String> backxml = new HashMap<String, String>();

		InputStream inStream;
		try {
			inStream = request.getInputStream();

			ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = inStream.read(buffer)) != -1) {
				outSteam.write(buffer, 0, len);
			}
			logger.error("微信支付----付款成功----");
			outSteam.close();
			inStream.close();
			String result = new String(outSteam.toByteArray(), "utf-8");// 获取微信调用我们notify_url的返回信息
			logger.error("微信支付----result----=" + result);
			Map<String, String> map = XCXUtils.readStringXmlOut(result);
			if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {
				logger.error("微信支付----返回成功");
				//必须验证微信官方回调过来的签名加密方式防止恶意请求
				if (Signature.verifyWeixinNotify(map)) {
                   //书写自己的业务逻辑
				}
				// ------------------------------
				// 处理业务完毕
				// ------------------------------
				BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
				out.write(resXml.getBytes());
				out.flush();
				out.close();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			logger.error("支付回调发布异常:" + e);
			e.printStackTrace();
		}
	}

是不是炒鸡简单哈哈哈哈哈 被我雷到了么?哈哈哈哈~~~~~~

--下面给大家分享一下我自己封装的实体类

代码语言:javascript
复制
/**
      * 预订单 Bean
      * @author KingYiFan
      */
    public class OrderInfo {
	private String appid;// 小程序ID
	private String mch_id;// 商户号
	private String nonce_str;// 随机字符串
	private String sign_type;//签名类型
	private String sign;// 签名
	private String body;// 商品描述
	private String out_trade_no;// 商户订单号
	private int total_fee;// 标价金额 ,单位为分
	private String spbill_create_ip;// 终端IP
	private String notify_url;// 通知地址
	private String trade_type;// 交易类型
	private String openid;//用户标识	

	public String getSign_type() {
		return sign_type;
	}

	public void setSign_type(String sign_type) {
		this.sign_type = sign_type;
	}

	public String getOpenid() {
		return openid;
	}

	public void setOpenid(String openid) {
		this.openid = openid;
	}

	public String getAppid() {
		return appid;
	}

	public void setAppid(String appid) {
		this.appid = appid;
	}

	public String getMch_id() {
		return mch_id;
	}

	public void setMch_id(String mch_id) {
		this.mch_id = mch_id;
	}

	public String getNonce_str() {
		return nonce_str;
	}

	public void setNonce_str(String nonce_str) {
		this.nonce_str = nonce_str;
	}

	public String getSign() {
		return sign;
	}

	public void setSign(String sign) {
		this.sign = sign;
	}

	public String getBody() {
		return body;
	}

	public void setBody(String body) {
		this.body = body;
	}

	public String getOut_trade_no() {
		return out_trade_no;
	}

	public void setOut_trade_no(String out_trade_no) {
		this.out_trade_no = out_trade_no;
	}

	public int getTotal_fee() {
		return total_fee;
	}

	public void setTotal_fee(int total_fee) {
		this.total_fee = total_fee;
	}

	public String getSpbill_create_ip() {
		return spbill_create_ip;
	}

	public void setSpbill_create_ip(String spbill_create_ip) {
		this.spbill_create_ip = spbill_create_ip;
	}

	public String getNotify_url() {
		return notify_url;
	}

	public void setNotify_url(String notify_url) {
		this.notify_url = notify_url;
	}

	public String getTrade_type() {
		return trade_type;
	}

	public void setTrade_type(String trade_type) {
		this.trade_type = trade_type;
	 }
    }

下面是封装签名的工具类。。。
代码语言:javascript
复制
  /**
	 * 签名算
	 * @param o
	 * 要参与签名的数据对象
	 * @return 签名
	 * @throws IllegalAccessException
	 */
	public static String getSign(Object o) throws IllegalAccessException {
		ArrayList<String> list = new ArrayList<String>();
		Class cls = o.getClass();
		Field[] fields = cls.getDeclaredFields();
		for (Field f : fields) {
			f.setAccessible(true);
			if (f.get(o) != null && f.get(o) != "") {
				String name = f.getName();
				XStreamAlias anno = f.getAnnotation(XStreamAlias.class);
				if (anno != null)
					name = anno.value();
				list.add(name + "=" + f.get(o) + "&");
			}
		}
		int size = list.size();
		String[] arrayToSort = list.toArray(new String[size]);
		Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < size; i++) {
			sb.append(arrayToSort[i]);
		}
		String result = sb.toString();
		result += "key=" + Configure.getKey();
		System.out.println("签名数据:" + result);
		result = MD5.MD5Encode(result).toUpperCase();
		return result;
	}

(adsbygoogle =window.adsbygoogle ||[]).push({});

签名实体类----
代码语言:javascript
复制
/**
	 * 签名信息
	 * @author KingYiFan
	 *
	 */
	public class SignInfo {

	private String appId;//小程序ID
	private String timeStamp;//时间戳
	private String nonceStr;//随机串
	@XStreamAlias("package")
	private String repay_id;
	private String signType;//签名方式
	public String getAppId() {
		return appId;
	}
	public void setAppId(String appId) {
		this.appId = appId;
	}
	public String getTimeStamp() {
		return timeStamp;
	}
	public void setTimeStamp(String timeStamp) {
		this.timeStamp = timeStamp;
	}
	public String getNonceStr() {
		return nonceStr;
	}
	public void setNonceStr(String nonceStr) {
		this.nonceStr = nonceStr;
	}
	public String getRepay_id() {
		return repay_id;
	}
	public void setRepay_id(String repay_id) {
		this.repay_id = repay_id;
	}
	public String getSignType() {
		return signType;
	}
	public void setSignType(String signType) {
		this.signType = signType;
	}
	}

生成随机数的工具类

代码语言:javascript
复制
	/**
     * 获取一定长度的随机字符串
     * @param length 指定字符串长度
     * @return 一定长度的字符串
     * @author KingYiFan
     */
    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();
    }
	}

httpclient 发送post工具类

代码语言:javascript
复制
//连接超时时间,默认10秒
    private static final int socketTimeout = 10000;

    //传输超时时间,默认30秒
    private static final int connectTimeout = 30000;
	/**
	 * post请求
	 * @throws IOException 
	 * @throws ClientProtocolException 
	 * @throws NoSuchAlgorithmException 
	 * @throws KeyStoreException 
	 * @throws KeyManagementException 
	 * @throws UnrecoverableKeyException 
	 */
	public static String sendPost(String url, Object xmlObj) throws Exception {

		HttpPost httpPost = new HttpPost(url);
		//解决XStream对出现双下划线的bug
        XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
        xStreamForRequestPostData.alias("xml", xmlObj.getClass());
        //将要提交给API的数据对象转换成XML格式数据Post给API
        String postDataXML = xStreamForRequestPostData.toXML(xmlObj);

        //得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);

        //设置请求器的配置
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
        httpPost.setConfig(requestConfig);
        HttpClient httpClient = HttpClients.createDefault();
        HttpResponse response = httpClient.execute(httpPost);
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity, "UTF-8");
        return result;
	}

微信支付回调认证是否是微信官方请求工具类

代码语言:javascript
复制
	/**
	 * 验证回调是否是微信官方
	 * @param map
	 * @return
	 * KingYiFan
	 */
	public static boolean verifyWeixinNotify(Map<String, String> map) {
		SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
		String sign = (String) map.get("sign");
		for (Object keyValue : map.keySet()) {
			if (!keyValue.toString().equals("sign")) {
				parameterMap.put(keyValue.toString(), map.get(keyValue).toString());
			}
		}
		String createSign = getSign(parameterMap);
		if (createSign.equals(sign)) {
			return true;
		} else {
			return false;
		}
	}```java
	/**
	 * 验证回调是否是微信官方
	 * @param map
	 * @return
	 * KingYiFan
	 */
	public static boolean verifyWeixinNotify(Map<String, String> map) {
		SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
		String sign = (String) map.get("sign");
		for (Object keyValue : map.keySet()) {
			if (!keyValue.toString().equals("sign")) {
				parameterMap.put(keyValue.toString(), map.get(keyValue).toString());
			}
		}
		String createSign = getSign(parameterMap);
		if (createSign.equals(sign)) {
			return true;
		} else {
			return false;
		}
	}

xml转换map工具类

代码语言:javascript
复制
/**
	 * @description 将xml字符串转换成map
	 * @param xml
	 * @return Map
	 */
	public static Map<String, String> readStringXmlOut(String xml) {
		Map<String, String> map = new HashMap<String, String>();
		Document doc = null;
		try {
			doc = DocumentHelper.parseText(xml); // 将字符串转为XML
			Element rootElt = doc.getRootElement(); // 获取根节点
			List<Element> list = rootElt.elements();// 获取根节点下所有节点
			for (Element element : list) { // 遍历节点
				map.put(element.getName(), element.getText()); // 节点的name为map的key,text为map的value
			}
		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return map;
	}

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-08-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 上面的代码只是调起来微信支付,下面我再说一下用户支付成功回调
  • --下面给大家分享一下我自己封装的实体类
    • 下面是封装签名的工具类。。。
      • 签名实体类----
      • httpclient 发送post工具类
      • 微信支付回调认证是否是微信官方请求工具类
      • xml转换map工具类
      相关产品与服务
      云开发 CloudBase
      云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档