当时做微信H5支付的时候,查询网上资料确实不是很多,都是断断续续,并不完整,花了两天时间,把支付搞定了,写个博客记录一下,也希望朋友少走点坑,代码是完全没问题的,现在把代码一一贴出来.强调一下是微信H5支付,不是公众号支付,不是app支付(当然你app是H5做的就可以)
这是html部分页面
<#include "header.html"/>
<body>
<header>
<div class="goback">
<a href="${contextPath}/app/PersonalCenter/index">
<i class="icon-leftarrow"></i>
</a>
</div>
<div class="top_cen">
充值
</div>
<div class="head_new" style="width: 6rem;">
<a href="javascript:chongzhijl();">
充值记录
</a>
</div>
</header>
<!--结束头部-->
<!--S 中间部分-->
<div class="cen_content">
<div class="pers_recharge">
<div class="leixing recharge_lanmu">
<h3>充值类型 <small>(1元现金兑换10X币)</small></h3>
<label class="checkbox-inline">
<input type="radio" id="inlineCheckbox1" value="2" name="czstyle" checked="checked"> 现金
</label>
<label class="checkbox-inline">
<input type="radio" id="inlineCheckbox2" value="1" name="czstyle"> X币
</label>
</div>
<div class="jine recharge_lanmu">
<h3>充值金额</h3>
<div class="JEmoney"><input type="number" name="money" value="" placeholder="输入金额" id="money"> 元</div>
</div>
<div class="fangshi recharge_lanmu">
<h3>充值方式</h3>
<div class="FS_xuanzhe">
<ul>
<li id="alipay" class="act">
<!--<i class="icon-weixin"></i>-->
<!--<span>微信</span>-->
<img src="${contextPath}/html/app/images/sec-img.jpg" />
</li>
<li id="weichat">
<!--<i class="icon-sina"></i>-->
<!--<span>支付宝</span>-->
<img src="${contextPath}/html/app/images/weixinzf.jpg" />
</li>
</ul>
</div>
</div>
<div class="tijiao">
<button type="button" class="btn btn-success" οnclick="moneyChongzhi();">确定充值</button>
</div>
</div>
</div>
<!--E 中间部分-->
<!--S 底部 -->
<#include "index_footer.html"/>
<!--E 底部 -->
<!--S 登录 -->
<#include "login.html"/>
<!--E 登录 -->
<script>
$(function(){
$(".FS_xuanzhe>ul>li").on("click",function(){
$(this).addClass("act").siblings().removeClass("act");
})
})
Android.appWellOut(0);
//提交申请
function moneyChongzhi(){
var msg = [];
var num = $("#money").val();
if("" == num || "0" == num){
$.dialog.alert("请输入充值金额!");
return;
}
if(parseFloat(num) < 0){
$.dialog.alert("请输入正确金额!");
return;
}
// 充值方式
var kind = $("#alipay[class='act']").length;
// 充值类型
var czstyle = $('input[name="czstyle"]:checked').val();
// 验证登录
var requesturl = "${contextPath}/app/islogin";
TC.util.postAjaxData(requesturl, {}, function(data){
if(data.login){
// 已登录,跳转到支付页面
var toUrl = "";
if("1" == kind){
// 支付宝
toUrl = "${contextPath}/app/alipay/index?WIDtotal_fee="+num+"&czstyle="+czstyle;
} else {
// 微信
toUrl = "${contextPath}/app/alipay/wxPayH5?czstyle="+czstyle+"&WIDtotal_fee="+num;
}
window.location.assign(toUrl);
} else {
// 未登录,弹出登录页面
$(".popup").show();
// 设置登陆成功后需要访问的地址
var toUrl = "";
if("1" == kind){
// 支付宝
toUrl = "${contextPath}/app/alipay/index?WIDtotal_fee="+num+"&czstyle="+czstyle;
} else {
// 微信
toUrl = "${contextPath}/app/alipay/index?WIDtotal_fee="+num+"&czstyle="+czstyle;
}
$("#toUrl").val(toUrl);
// 是否需要跳转页面。 0跳转页面,1不跳转页面
$("#isJump").val("0");
}
}, true, "POST");
}
//充值记录查询
function chongzhijl(){
// 验证登录
var requesturl = "${contextPath}/app/islogin";
TC.util.postAjaxData(requesturl, {}, function(data){
if(data.login){
// 已登录,跳转到查询页面
var toUrl = "${contextPath}/app/mywallet/my_waRecord";
window.location.assign(toUrl);
} else {
// 未登录,弹出登录页面
$(".popup").show();
// 设置登陆成功后需要访问的地址
var toUrl = "${contextPath}/app/mywallet/my_waRecord";
$("#toUrl").val(toUrl);
// 是否需要跳转页面。 0跳转页面,1不跳转页面
$("#isJump").val("0");
}
}, true, "POST");
}
</script>
</body>
</html>
点击确认充值进入后台(地址: “${contextPath}/app/alipay/wxPayH5?czstyle=”+czstyle+”&WIDtotal_fee=”+num;)
package com.rtrh.resourceManager.controller.app;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import com.rtrh.resourceManager.com.alipay.config.AlipayConfig;
import com.rtrh.resourceManager.com.alipay.util.AlipayNotify;
import com.rtrh.resourceManager.com.alipay.util.AlipaySubmit;
import com.rtrh.resourceManager.com.weixin.config.PayConfig;
import com.rtrh.resourceManager.com.weixin.util.HttpUtil;
import com.rtrh.resourceManager.com.weixin.util.PayCommonUtil;
import com.rtrh.resourceManager.com.weixin.util.XMLUtil;
import com.rtrh.resourceManager.controller.BaseController;
import com.rtrh.resourceManager.exception.BizException;
import com.rtrh.resourceManager.parameter.ResourceConstants;
import com.rtrh.resourceManager.service.UserIncomeFlowService;
import com.rtrh.resourceManager.service.UserIncomeService;
import com.rtrh.resourceManager.service.web.person.MyWalletService;
import com.rtrh.resourceManager.tools.LogUtil;
import com.rtrh.resourceManager.tools.RandomGUID;
/**
* @author huangshouyi
* @descrition 手机网站支付接口接入页controller 2016年7月4日 下午10:40:12
*/
@Controller
@RequestMapping("/app/alipay")
public class AppAliPayController extends BaseController {
@Autowired
UserIncomeFlowService userIncomeFlowService;
@Autowired
UserIncomeService userIncomeService;
@Autowired
MyWalletService myWalletService;
// 充值标识
private Boolean czflg;
// 充值类型:1 积分 2 现金
private Integer czstyle = 0;
/**
* 微信H5支付
*
* @param request
* @param response
* @param model
* @throws Exception
*/
@RequestMapping("/wxPayH5")
public void wxPayH5(HttpServletRequest request, HttpServletResponse response, ModelMap model) throws Exception {
Map<String, Object> result = new HashMap<String, Object>();
result.put("success", false);
try {
// 付款金额,必填
String total_fee = request.getParameter("WIDtotal_fee");
// 充值类型:1 积分 2 现金
czstyle = Integer.valueOf(request.getParameter("czstyle"));
// ip地址获取
String basePath = request.getServerName() + ":" + request.getServerPort();
// 账号信息
String appid = PayConfig.APP_ID; // appid
String mch_id = PayConfig.MCH_ID; // 商业号
String key = PayConfig.API_KEY; // key
String currTime = PayCommonUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = PayCommonUtil.buildRandom(4) + "";
String nonce_str = strTime + strRandom;
// 价格 注意:价格的单位是分
String order_price = new BigDecimal(total_fee).multiply(new BigDecimal(100)).toString().split("\\.")[0];
// 自己网站上的订单号
RandomGUID id = new RandomGUID();
String out_trade_no = id.toString().substring(0, 32);
// 获取发起电脑 ip
String spbill_create_ip = HttpUtil.getRealIp(request);
// 回调接口
String notify_url = PayConfig.NOTIFY_URL_H5.replaceAll("localhostUrl", basePath) + getCurrentUser().getId();
// 页面跳转同步通知页面路径
String trade_type = "MWEB";
// 设置package订单参数
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
packageParams.put("appid", appid);
packageParams.put("mch_id", mch_id);
// 生成签名的时候需要你自己设置随机字符串
packageParams.put("nonce_str", nonce_str);
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("total_fee", order_price);
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
packageParams.put("body", PayConfig.BODY);
packageParams.put("scene_info", "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"http://www.xxxx.com\",\"wap_name\": \"学易资源分享平台\"}}");
String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
packageParams.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(packageParams);
String resXml = HttpUtil.postData(PayConfig.UFDODER_URL, requestXML);
Map map = XMLUtil.doXMLParse(resXml);
String urlCode = (String) map.get("code_url");
//确认支付过后跳的地址,需要经过urlencode处理
String urlString = URLEncoder.encode("http://www.xxxx.com/xxxxx/my_waRecord", "GBK");
String mweb_url = map.get("mweb_url")+"&redirect_url="+urlString;
czflg = true;
response.sendRedirect(mweb_url);
LogUtil.writeMsgToFile("before ----" + czflg);
result.put("sHtmlText", urlCode);
result.put("success", true);
} catch (Exception e) {
LogUtil.writeMsgToFile(e);
result.put("errormsg", e.getMessage());
}
}
/**
* 执行回调 确认支付后处理事件 例如添加金额到数据库等操作
*
* @param request
* @param response
* @throws Exception
*/
@RequestMapping("/payNotify")
public void weixin_notify(HttpServletRequest request, HttpServletResponse response, ModelMap model)throws Exception {
System.out.println("进入支付h5回调=====================");
String xmlMsg = readData(request);
System.out.println("pay notice---------"+xmlMsg);
Map params = XMLUtil.doXMLParse(xmlMsg);
// String appid = params.get("appid");
// //商户号
// String mch_id = params.get("mch_id");
String result_code = params.get("result_code")+"";
// String openId = params.get("openid");
// //交易类型
// String trade_type = params.get("trade_type");
// //付款银行
// String bank_type = params.get("bank_type");
// // 总金额
// String total_fee = params.get("total_fee");
// //现金支付金额
// String cash_fee = params.get("cash_fee");
// // 微信支付订单号
// String transaction_id = params.get("transaction_id");
// // 商户订单号
// String out_trade_no = params.get("out_trade_no");
// // 支付完成时间,格式为yyyyMMddHHmmss
// String time_end = params.get("time_end");
/以下是附加参数///
String attach = params.get("attach")+"";
// String fee_type = params.get("fee_type");
// String is_subscribe = params.get("is_subscribe");
// String err_code = params.get("err_code");
// String err_code_des = params.get("err_code_des");
String userid = null;
try {
// 过滤空 设置 TreeMap
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
Iterator it = params.keySet().iterator();
while (it.hasNext()) {
String parameter = (String) it.next();
String parameterValue = params.get(parameter)+"";
String v = "";
if (null != parameterValue) {
v = parameterValue.trim();
}
System.out.println("value==============="+v);
packageParams.put(parameter, v);
}
// 查看回调参数
// LogUtil.writeMsgToFile(packageParams.toString());
String total_fee = new BigDecimal((String) packageParams.get("total_fee")).divide(new BigDecimal(100)).toString();
userid = (String) packageParams.get("userid");
// 账号信息
String resXml = "";
// ------------------------------
// 处理业务开始
// ------------------------------
if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
// 这里是支付成功
czflg = true;
System.out.println("支付成功============"+czflg+"czstyle========="+czstyle);
// 执行自己的业务逻辑
model.put("sHtmlText", "充值成功");
try {
synchronized (czflg) {
if (czflg) {
// 充值积分
if (czstyle.equals(1)) {
userIncomeFlowService.saveMoneyFlowChongzhi(total_fee, userid, false);
} else if (czstyle.equals(2)) {
// 充值 现金
userIncomeFlowService.saveMoneyFlowChongzhi(total_fee, userid);
}
czflg = false;
LogUtil.writeMsgToFile("end ----" + czflg);
}
LogUtil.writeMsgToFile("enderror ----" + czflg);
}
} catch (BizException e) {
LogUtil.writeMsgToFile(e);
}
// 执行自己的业务逻辑
// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
model.put("sHtmlText", "充值失败");
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[充值失败]]></return_msg>" + "</xml> ";
}
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
LogUtil.writeMsgToFile(e);
}
}
public static String readData(HttpServletRequest request) {
BufferedReader br = null;
try {
StringBuilder result = new StringBuilder();
br = request.getReader();
for (String line; (line=br.readLine())!=null;) {
if (result.length() > 0) {
result.append("\n");
}
result.append(line);
}
return result.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
finally {
if (br != null)
try {br.close();} catch (IOException e) {e.printStackTrace();}
}
}
}
这个类是微信初始化参数的类 PayConfig
package com.rtrh.resourceManager.com.weixin.config;
/**
* 支付参数
*
* @author huangshouyi
*
*/
public class PayConfig {
// 微信号
public static String APP_ID = "wxexxxxxx";
// 应用对应的凭证
public static String APP_SECRET = "138xxxxxx";
// 商户密钥
public static String API_KEY = "d13143xxxxxx";
// 商业号
public static String MCH_ID = "13826xxxx";
// 回调地址
public static String NOTIFY_URL = "http://localhostUrl/resourceManager/xxxxxx?userid=";
//微信支付h5 回调地址
public static String NOTIFY_URL_H5 = "http://localhostUrl/resourceManager/xxxxxx?userid=";
// 商品名称
public static String BODY = "xxxxx共享平台系统充值 ";
// 请求地址
public static String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// 微信支付V2账单查询接口
public static String ORDERQUERY = "https://api.mch.weixin.qq.com/pay/orderquery";
}
这是获取签名等信息的一个类 PayCommonUtil
package com.rtrh.resourceManager.com.weixin.util;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import com.rtrh.resourceManager.tools.LogUtil;
public class PayCommonUtil {
/**
* 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
*
* @return boolean
*/
public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
StringBuffer sb = new StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (!"sign".equals(k) && null != v && !"".equals(v)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
// 算出摘要
String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
String tenpaySign = ((String) packageParams.get("sign")).toLowerCase();
// System.out.println(tenpaySign + " " + mysign);
return tenpaySign.equals(mysign);
}
/**
* @author
* @date 2016-4-22
* @Description:sign签名
* @param characterEncoding
* 编码格式
* @param parameters
* 请求参数
* @return
*/
public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
StringBuffer sb = new StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
/**
* @author
* @date 2016-4-22
* @Description:将请求参数转换为xml格式的string
* @param parameters
* 请求参数
* @return
*/
public static String getRequestXml(SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
} else {
sb.append("<" + k + ">" + v + "</" + k + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* 取出一个指定长度大小的随机正整数.
*
* @param length
* int 设定所取出随机数的长度。length小于11
* @return int 返回生成的随机数。
*/
public static int buildRandom(int length) {
int num = 1;
double random = Math.random();
if (random < 0.1) {
random = random + 0.1;
}
for (int i = 0; i < length; i++) {
num = num * 10;
}
return (int) ((random * num));
}
/**
* 获取当前时间 yyyyMMddHHmmss
*
* @return String
*/
public static String getCurrTime() {
Date now = new Date();
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String s = outFormat.format(now);
return s;
}
}
工具类 HttpUtil
package com.rtrh.resourceManager.com.weixin.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
/**
* Http客户端工具类<br/>
* 这是内部调用类,请不要在外部调用。
*
* @author miklchen
*
*/
public class HttpUtil {
private final static int CONNECT_TIMEOUT = 5000; // in milliseconds
private final static String DEFAULT_ENCODING = "UTF-8";
public static String postData(String urlStr, String data) {
return postData(urlStr, data, null);
}
public static String postData(String urlStr, String data, String contentType) {
BufferedReader reader = null;
try {
URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(CONNECT_TIMEOUT);
if (contentType != null)
conn.setRequestProperty("content-type", contentType);
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
if (data == null)
data = "";
writer.write(data);
writer.flush();
writer.close();
reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
sb.append("\r\n");
}
return sb.toString();
} catch (IOException e) {
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e) {
}
}
return null;
}
/**
* 获取真实ip地址 通过阿帕奇代理的也能获取到真实ip
* @param request
* @return
*/
public static String getRealIp(HttpServletRequest request) {
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();
}
return ip;
}
}
xml工具类 XMLUtil
package com.rtrh.resourceManager.com.weixin.util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
/**
* xml工具类
*
* @author miklchen
*
*/
public class XMLUtil {
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
*
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
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 = XMLUtil.getChildrenText(children);
}
m.put(k, v);
}
// 关闭流
in.close();
return m;
}
/**
* 获取子结点的xml
*
* @param children
* @return String
*/
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(XMLUtil.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
}
这样基本上就大功告成,只要你微信的参数没问题,绝对可以正常跑起来,使用微信外部浏览器就可以调出微信支付,有什么不清楚的可以在下面留言.做这个功能我还去下了csdn一些demo,花了我好多积分,现在我无偿分享出来哈.
需要demo的可以去我发布出来的资源下载,积分很便宜,我集合了两个大神的demo,当时花了我三十多积分,这是下载地址:https://download.csdn.net/download/leigelg/10438468
版权声明:本文为leigelg原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。