JAVA实现微信支付功能

一,问题

这两天,需要接入微信支付功能。由于我是公众号开发,因此,我选择的微信支付方式是JSAPI支付方式。

二,解决方案

2.1 配置微信平台

①配置微信公众平台

登录微信公众平台=》公众号设置=》功能设置=》网页授权域名

在这里插入图片描述

在这里插入图片描述

②配置微信商家平台

产品中心=》开发配置

在这里插入图片描述

在这里插入图片描述

2.2 后台代码的实现

JSAPI官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

①先去官方下载SDK,并导进项目中

地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

在这里插入图片描述

②准备好11个参数

appid:商家平台ID。在微信的平台上有
body:商品描述。
mch_id:商户ID。在微信的平台上有
nonce_str:随机字符串,UUID就好了。
openid:用户标识。因为这边是用户已经登录成功了。所以在session中就能拿到。
out_trade_no:商户订单号
spbill_create_ip:终端IP。这个可以从请求头中拿到
total_fee:支付金额。单位是分。
trade_type:交易类型。这里我填JSAPI
notify_url:通知地址。就是用户支付成功之后,微信访问你的哪个接口,跟你传递支付成功的相关信息。
sign:签名。这个签名它是由上面的10个参数计算得出的。

③源码
sendPay类:

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

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

import org.apache.http.auth.AUTH;
import org.hamcrest.core.Is;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.jc.util.wxPay.WXPayUtil;
import com.sun.xml.internal.fastinfoset.Encoder;

import controller.AuthUtil;
import net.sf.json.JSONObject;

@Controller
@RequestMapping("/pay")
public class sendPay {
 
	/**
	 * @Description 微信浏览器内微信支付/公众号支付(JSAPI)
	 * @param request
	 * @param code
	 * @return Map
	 */
	@RequestMapping(value = "orders")
	public @ResponseBody Map<String, String> orders(HttpServletRequest request, HttpServletResponse response) {
		try {
			 
			String openId = "用户的openid";

			// 拼接统一下单地址参数
			Map<String, String> paraMap = new HashMap<String, String>();
			// 获取请求ip地址
			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.getRemoteAddr();
			}
			if (ip.indexOf(",") != -1) {
				String[] ips = ip.split(",");
				ip = ips[0].trim();
			}

			paraMap.put("appid", AuthUtil.APPID); // 商家平台ID
			paraMap.put("body", "纯情小店铺-薯条"); // 商家名称-销售商品类目、String(128)
			paraMap.put("mch_id", AuthUtil.MCHID); // 商户ID
			paraMap.put("nonce_str", WXPayUtil.generateNonceStr()); // UUID
			paraMap.put("openid", openId);
			paraMap.put("out_trade_no", UUID.randomUUID().toString().replaceAll("-", ""));// 订单号,每次都不同
			paraMap.put("spbill_create_ip", ip);
			paraMap.put("total_fee", "1"); // 支付金额,单位分
			paraMap.put("trade_type", "JSAPI"); // 支付类型
			paraMap.put("notify_url", "用户支付完成后,你想微信调你的哪个接口");// 此路径是微信服务器调用支付结果通知路径随意写
			String sign = WXPayUtil.generateSignature(paraMap, AuthUtil.PATERNERKEY);
			paraMap.put("sign", sign);
			String xml = WXPayUtil.mapToXml(paraMap);// 将所有参数(map)转xml格式

			// 统一下单 https://api.mch.weixin.qq.com/pay/unifiedorder
			String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

			System.out.println("xml为:" + xml);

			// String xmlStr = HttpRequest.sendPost(unifiedorder_url,
			// xml);//发送post请求"统一下单接口"返回预支付id:prepay_id

			String xmlStr = HttpRequest.httpsRequest(unifiedorder_url, "POST", xml);

			System.out.println("xmlStr为:" + xmlStr);

			// 以下内容是返回前端页面的json数据
			String prepay_id = "";// 预支付id
			if (xmlStr.indexOf("SUCCESS") != -1) {
				Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
				prepay_id = (String) map.get("prepay_id");
			}

			Map<String, String> payMap = new HashMap<String, String>();
			payMap.put("appId", AuthUtil.APPID);
			payMap.put("timeStamp", WXPayUtil.getCurrentTimestamp() + "");
			payMap.put("nonceStr", WXPayUtil.generateNonceStr());
			payMap.put("signType", "MD5");
			payMap.put("package", "prepay_id=" + prepay_id);
			String paySign = WXPayUtil.generateSignature(payMap, AuthUtil.PATERNERKEY);
			payMap.put("paySign", paySign);
			//将这个6个参数传给前端
			return payMap;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * @Title: callBack
	 * @Description: 支付完成的回调函数
	 * @param:
	 * @return:
	 */
	@RequestMapping("/notify")
	public String callBack(HttpServletRequest request, HttpServletResponse response) {
		// System.out.println("微信支付成功,微信发送的callback信息,请注意修改订单信息");
		InputStream is = null;
		try {

			is = request.getInputStream();// 获取请求的流信息(这里是微信发的xml格式所有只能使用流来读)
			String xml = WXPayUtil.InputStream2String(is);
			Map<String, String> notifyMap = WXPayUtil.xmlToMap(xml);// 将微信发的xml转map
			
			System.out.println("微信返回给回调函数的信息为:"+xml);
			
			if (notifyMap.get("result_code").equals("SUCCESS")) {
				String ordersSn = notifyMap.get("out_trade_no");// 商户订单号
				String amountpaid = notifyMap.get("total_fee");// 实际支付的订单金额:单位 分
				BigDecimal amountPay = (new BigDecimal(amountpaid).divide(new BigDecimal("100"))).setScale(2);// 将分转换成元-实际支付金额:元

				/*
				 * 以下是自己的业务处理------仅做参考 更新order对应字段/已支付金额/状态码
				 */
				System.out.println("===notify===回调方法已经被调!!!");
				
			}
			
			// 告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
			response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) { 
					e.printStackTrace();
				}
			}
		}
		
		return null;
	}

}

HttpRequest类:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;

public class HttpRequest {
	/**
	 * 向指定URL发送GET方法的请求
	 * 
	 * @param url
	 *            发送请求的URL
	 * @param param
	 *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
	 * @return URL 所代表远程资源的响应结果
	 */
	public static String sendGet(String url, String param) {
		String result = "";
		BufferedReader in = null;
		try {
			String urlNameString = url + "?" + param;
			System.out.println(urlNameString);
			URL realUrl = new URL(urlNameString);
			// 打开和URL之间的连接
			URLConnection connection = realUrl.openConnection();
			// 设置通用的请求属性
			connection.setRequestProperty("accept", "*/*");
			connection.setRequestProperty("connection", "Keep-Alive");
			connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
			// 建立实际的连接
			connection.connect();
			// 获取所有响应头字段
			Map<String, List<String>> map = connection.getHeaderFields();
			// 遍历所有的响应头字段
			for (String key : map.keySet()) {
				System.out.println(key + "--->" + map.get(key));
			}
			// 定义 BufferedReader输入流来读取URL的响应
			in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			String line;
			while ((line = in.readLine()) != null) {
				result += line;
			}
		} catch (Exception e) {
			System.out.println("发送GET请求出现异常!" + e);
			e.printStackTrace();
		}
		// 使用finally块来关闭输入流
		finally {
			try {
				if (in != null) {
					in.close();
				}
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
		return result;
	}

	/**
	 * 向指定 URL 发送POST方法的请求
	 * 
	 * @param url
	 *            发送请求的 URL
	 * @param param
	 *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
	 * @return 所代表远程资源的响应结果
	 */
	public static String sendPost(String url, String param) {
		PrintWriter out = null;
		BufferedReader in = null;
		String result = "";
		try {
			URL realUrl = new URL(url);
			// 打开和URL之间的连接
			URLConnection conn = realUrl.openConnection();
			// 设置通用的请求属性
			conn.setRequestProperty("accept", "*/*");
			conn.setRequestProperty("connection", "Keep-Alive");
			conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
			// 发送POST请求必须设置如下两行
			conn.setDoOutput(true);
			conn.setDoInput(true);
			// 获取URLConnection对象对应的输出流
			out = new PrintWriter(conn.getOutputStream());
			// 发送请求参数
			out.print(param);
			// flush输出流的缓冲
			out.flush();
			// 定义BufferedReader输入流来读取URL的响应
			in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
			String line;
			while ((line = in.readLine()) != null) {
				result += line;
			}
		} catch (Exception e) {
			System.out.println("发送 POST 请求出现异常!" + e);
			e.printStackTrace();
		}
		// 使用finally块来关闭输出流、输入流
		finally {
			try {
				if (out != null) {
					out.close();
				}
				if (in != null) {
					in.close();
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
		return result;
	}

	/**
	 * post请求并得到返回结果
	 * 
	 * @param requestUrl
	 * @param requestMethod
	 * @param output
	 * @return
	 */
	public static String httpsRequest(String requestUrl, String requestMethod, String output) {
		try { 
			URL url = new URL(requestUrl);
			HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
			connection.setDoOutput(true);
			connection.setDoInput(true);
			connection.setUseCaches(false);
			connection.setRequestMethod(requestMethod);
			if (null != output) {
				OutputStream outputStream = connection.getOutputStream();
				outputStream.write(output.getBytes("UTF-8"));
				outputStream.close();
			}
			// 从输入流读取返回内容
			InputStream inputStream = connection.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
			String str = null;
			StringBuffer buffer = new StringBuffer();
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			bufferedReader.close();
			inputStreamReader.close();
			inputStream.close();
			inputStream = null;
			connection.disconnect();
			return buffer.toString();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return "";
	}
}

AuthUtil类

import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import net.sf.json.JSONObject;
public class AuthUtil {
	public static final String APPID = "平台ID";
	public static final String APPSECRET = "平台密钥";
	public static final String MCHID = "商家ID";
	public static final String PATERNERKEY = "商家密钥";
	
	
	public static JSONObject doGetJson(String url) throws ClientProtocolException, IOException {
		JSONObject jsonObject = null;
		// 首先初始化HttpClient对象
		DefaultHttpClient client = new DefaultHttpClient();
		// 通过get方式进行提交
		HttpGet httpGet = new HttpGet(url);
		// 通过HTTPclient的execute方法进行发送请求
		HttpResponse response = client.execute(httpGet);
		// 从response里面拿自己想要的结果
		HttpEntity entity = response.getEntity();
		if (entity != null) {
			String result = EntityUtils.toString(entity, "UTF-8");
			jsonObject = jsonObject.fromObject(result);
		}
		// 把链接释放掉
		httpGet.releaseConnection();
		return jsonObject;
	}
}

2.3 前端的实现

这是只用一个jsp页面来做测试

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="./js/jquery-1.11.0.min.js"></script>
<title>微信支付JSP</title>
</head>
<body> 
	<input type="button" value="进行微信支付" id="payId">

	<script type="text/javascript">
	
		$(function(){
			var appId,timeStamp,nonceStr,package,signType,paySign; 
			
			$("#payId").click(function(){
				pay();
			});
			
			//去后台拿六个参数
			function pay(){   
				var url = "http://localhost:8082/WeChat/pay/orders";
			  	$.get(url,function(result) {	
		  				appId = result.appId;
						timeStamp = result.timeStamp;
						nonceStr = result.nonceStr;
						package = result.package;
						signType = result.signType;
						paySign = result.paySign;
						
						if (typeof WeixinJSBridge == "undefined") {
							if (document.addEventListener) {
								document.addEventListener('WeixinJSBridgeReady',
										onBridgeReady, false);
							} else if (document.attachEvent) {
								document.attachEvent('WeixinJSBridgeReady',
										onBridgeReady);
								document.attachEvent('onWeixinJSBridgeReady',
										onBridgeReady);
							}
							alert("请在微信上进行支付操作!");
							onBridgeReady();
						} else { 
							onBridgeReady();
						}
					});
				}
			//去微信那边发起支付请求
			function onBridgeReady(){
				
				 alert("appId:"+appId+" "+"timeStamp:"+timeStamp+" "+"nonceStr:"+nonceStr+" "+"package:"+package+" "+"signType:"+signType+" "+"paySign:"+paySign+" ");
				
				  WeixinJSBridge.invoke( 'getBrandWCPayRequest', {
					  "appId":appId,     //公众号名称,由商户传入     
			          "timeStamp":timeStamp,         //时间戳,自1970年以来的秒数     
			          "nonceStr":nonceStr, //随机串     
			          "package":package,     
			          "signType":signType,         //微信签名方式:     
			          "paySign":paySign //微信签名 
			         }, 
			         function(res){      
			      	   if(res.err_msg == "get_brand_wcpay_request:ok" ) {
			                 //alert('支付成功');
			                 console.log("支付成功");
			                 //支付成功后跳转的页面
			             }else if(res.err_msg == "get_brand_wcpay_request:cancel"){
			            	 alert('支付取消');
			             }else if(res.err_msg == "get_brand_wcpay_request:fail"){
			            	 alert('支付失败');
			            	 
			            	 alert(JSON.stringify(res));
			            	 
			                 WeixinJSBridge.call('closeWindow');
			             } //使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
			   });    
			}
		})
</script>

</body>
</html>

三,总结

虽然第一次看官方文档很乱,信息量很多,但仔细总结一下,其实就下面这点流程而已。

在这里插入图片描述

最后,很感谢网上一位大哥分享了他的经验。
大哥链接:https://blog.csdn.net/javaYouCome/article/details/79473743

  • 50
    点赞
  • 291
    收藏
    觉得还不错? 一键收藏
  • 29
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值