手把手教你完成微信H5支付
关于微信支付支付,个人觉得相比支付宝复杂的太多了,但是慢慢理解起来还是很简单的
1,首先准备工作,下方是一个时序图,认真看,图看懂了做起来真的很简单
,
2,第二按照上图说明,开始下单时,调用微信下单接口是需要准备一下几个参数
APIKEY --------- 支付秘钥(微信商户平台可查,需要自己设置)
appid ----- 商户ID(微信商户平台可查)
body------- 商品名称
mch_id ----- 支付商户号(微信商户平台可查)
nonce_str ---------- 随机字符串
/**
* 获取随机字符串 Nonce Str
*
* @return String 随机字符串
*/
public static String generateNonceStr() {
char[] nonceChars = new char[32];
for (int index = 0 ; index < nonceChars.length ; ++index) {
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
}
return new String(nonceChars);
}
out_trade_no------------ 订单号
spbill_create_ip ------------请求IP
/**
* 获取用户实际ip
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
//根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
return ipAddress;
}
total_fee------------ //,字符串类型,获取金额,单位分
trade_type -------------------//支付类型,H5就是 "MWEB"
scene_info ------------------- //{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"https://www.chujian.live\",\"wap_name\": \"chongzhi\"}} 这个话里面 type就是Wap wap_url--指的是你们官网地址 ,wap_name 就是网站名称
notify_url ----------------- //此路径是微信服务器调用支付结果带了一大批参数多次请求
以下就把几个重要的工具类整理一下,后面会用到,建议务必下载微信开发文档,我这里只是把自己整理补充后的放上去
/**
* 生成签名
*
* @param data 待签名数据
* @param key API**
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, SignType.MD5);
}
/**
* XML格式字符串转换为Map
*
* @param strXML XML字符串
* @return XML数据转换后的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0 ; idx < nodeList.getLength() ; ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
throw ex;
}
}
/**
* 将Map转换为XML格式的字符串
*
* @param data Map类型数据
* @return XML格式的字符串
* @throws Exception
*/
public static String mapToXml(Map<String, String> data) throws Exception {
org.w3c.dom.Document document = WXPayXmlUtil.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key : data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
try {
writer.close();
} catch (Exception ex) {
}
return output;
}
/**
* 向指定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)");
conn.setRequestProperty("contentType", "text/xml");
conn.setRequestProperty("Charsert", "utf-8");
// 发送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()));
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;
}
3,接下来废话不多少了,直接上代码了
/**
* 用户充值金币----------微信H5支付
*/
@ResponseBody
@RequestMapping(value = "weixinPayRechargeGold", method = RequestMethod.GET)
public String weixinPayWap(HttpServletRequest request, HttpServletResponse response, Integer rechargeNumber, String rechargeId) {
String mweb_url = "";//跳转链接
//如果是模拟请求则返回null
boolean bool = rechargeList.contains(rechargeId);
if (bool == false) {
return null;
}
try {
//保存充值记录到数据库
//此处写自己的业务
//拼接统一下单地址参数
Map<String, String> paraMap = new HashMap<String, String>();
//获取请求ip地址
String ip = WXPayUtil.getIpAddr(request);
String bodyName = "MEIDAO" + mdRecharge.getNumber();
Integer money = mdRecharge.getRmb() * 100;//获取金额,单位分
paraMap.put("appid", WXPayConstants.APPID); //商户ID
paraMap.put("body", bodyName); //商品名称
paraMap.put("mch_id", WXPayConstants.MCHID);
paraMap.put("nonce_str", WXPayUtil.generateNonceStr()); //String 随机字符串
paraMap.put("out_trade_no", mdRecharge.getNumber());//订单号
paraMap.put("spbill_create_ip", ip);//请求IP
paraMap.put("total_fee", money.toString()); //加钱
paraMap.put("trade_type", "MWEB"); //类型
paraMap.put("scene_info", "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"https://www.chujian.live\",\"wap_name\": \"yinyinchongzhi\"}}");
paraMap.put("notify_url", "https://www.chujian.live/faint-service/f/weixin/weixinRechargeGoldResult");// 此路径是微信服务器调用支付结果带了一大批参数多次请求
String paternerKey = WXPayConstants.APIKEY;
String sign = WXPayUtil.generateSignature(paraMap, 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";
String xmlStr = HttpRequest.sendPost(unifiedorder_url, xml);
//以下内容是返回前端页面的json数据
if (xmlStr.indexOf("SUCCESS") != -1) {
Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
mweb_url = (String) map.get("mweb_url");
//支付完返回浏览器跳转的地址,如跳到查看订单页面
String redirect_url = "******此处写自己的域名******/faint-service/static/h5/app/successh5.html";
String redirect_urlEncode = URLEncoder.encode(redirect_url, "utf-8");//对上面地址urlencode
mweb_url = mweb_url + "&redirect_url=" + redirect_urlEncode;//拼接返回地址
}
} catch (Exception e) {
e.printStackTrace();
}
return mweb_url;
}
/**
* 用户充值金币----------微信h5返回结果
*/
@RequestMapping("weixinRechargeGoldResult")
public void weixinResult(HttpServletRequest request, HttpServletResponse response) {
BufferedReader reader;
try {
reader = request.getReader();
String line = "";
StringBuffer inputString = new StringBuffer();
while ((line = reader.readLine()) != null) {
inputString.append(line);
}
request.getReader().close();
Map<String, String> notifyMap = WXPayUtil.xmlToMap(inputString.toString());
if (notifyMap.get("return_code").equals("SUCCESS")) {
if (notifyMap.get("result_code").equals("SUCCESS")) {
String orderSn = notifyMap.get("out_trade_no"); //商户订单号
String amountpaid = notifyMap.get("total_fee");//实际支付的订单金额:单位 分
BigDecimal amountPay = (new BigDecimal(amountpaid).divide(new BigDecimal("100"))).setScale(2);//将分转换成元-实际支付金额:元
String openid = notifyMap.get("openid"); //如果有需要可以获取
///此处写自己的业务
}
}
//告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
} catch (Exception e) {
e.printStackTrace();
}
}
如有问题,请指教.有问题也可以给楼主留言