开发之前请详细阅读微信公众号支付开发文档,并配置好服务器信息。
微信支付开发文档
1.微信支付申请,开通微信商户平台。
2.开发配置,填写服务器地址。
3.在开发电脑上安装证书,用于设置获取商户秘钥。
4.设置获取秘钥。
开始开发
1.用户点击下面页面自动跳转到微信授权链接。
index.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
head>
<body>
<script type="text/javascript">
//用户点击跳转地址(非静默授权) 参数appid为公众号的id redirect_uri为微信回调接口 state为可携带的参数
window.location.href="https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=http://你的域名/weChatpay/mainServlet&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect";
script>
body>
html>
2.微信回调处理函数。
MainServlet.java
package com.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import com.utils.CommonUtil;
public class MainServlet extends HttpServlet {
//网页授权获取用户信息
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String appid = "";//公众号appid
String appsecret = "";//公众号秘钥
//微信返回的code
String code = request.getParameter("code");
//获取openId和access_token(获取openId后调用统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder)
String openId = "";
String access_token = "";
String URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appid+"&secret="+appsecret+"&code="+code+"&grant_type=authorization_code";
JSONObject jsonObject = CommonUtil.httpsRequest(URL, "GET", null);
if (null != jsonObject) {
openId = jsonObject.getString("openid");
access_token = jsonObject.getString("access_token");
}
request.getSession().setAttribute("openId", openId);
//获取用户信息
String nickname = "";
String headimgurl = "";
String userURL = "https://api.weixin.qq.com/sns/userinfo?access_token="+access_token+"&openid="+openId+"";
JSONObject userJson = CommonUtil.httpsRequest(userURL, "GET", null);
if (null != jsonObject) {
nickname = userJson.getString("nickname");
headimgurl = userJson.getString("headimgurl");
}
request.getSession().setAttribute("nickname", nickname);
request.getSession().setAttribute("headimgurl", headimgurl);
response.sendRedirect("shop/index.jsp");//跳转到支付页面
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
3.支付页面ajax请求接口获取微信网页调起微信支付的相关参数(详见文档),用户点击购买的时候,请求接口获取支付参数。
shop/index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" %>
<% String path=request.getContextPath(); String basePath=request.getScheme()+ "://"+request.getServerName()+ ":"+request.getServerPort()+path+ "/"; %>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no" />
<title>微信商城title>
<link href="css/style.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="touchTouch/touchTouch.css">
<link rel="stylesheet" href="css/pintuer.css">
<script type="text/javascript" src="js/jquery.min.js">script>
<script src="js/script.js">script>
<script src="js/jquery.js">script>
<script src="js/jquery-1.7.1.min.js">script>
<script src="touchTouch/touchTouch.jquery.js">script>
<script type="text/javascript" src="js/jquery.Spinner.js">script>
<script type="text/javascript">//参与人数加减
$(function() {
$("#d").Spinner();
$('.thumbs-img').each(function() {
$(this).children('a').touchTouch();
});
});
//点击图片
$('.thumbs-img').each(function() {
9 * $(this).children('a').touchTouch();
});
script>
head>
<body>
<section>
<banner>
<img src="img/ban.jpg">
<yiny>yiny>
banner>
<div class="jindutiao">
<p>第一期:上海迪士尼门票p>
<progress value="" max="">progress>
<div class="canshu">
<div class="canshu_l">
<font>font>
<p>已参与人次p>
div>
<div class="canshu_z">
<font>font>
<p>总需人次p>
div>
<div class="canshu_r">
<font>font>
<p>剩余人次p>
div>
div>
div>
<div class="centerq">
<div class="center">
<p>我要参与p>
<div id="d" class="Spinner">div>
<p>人购买人次越多获得几率越大哦!p>
div>
<div class="canyu">
<p>购买p>
div>
div>
<div id="cen_right_top">
<h3 class="active">商品介绍h3>
<h3>夺宝记录h3>
<h3>用户晒单h3>
<div style="display:block" class="tab_show">
<div class="know">
<p>您需要知道的"香港迪士尼乐园"p>
<ul>
<li>
<span>特色1span> 迪士尼全球的第十一个主题乐园li>
<li>
<span>特色2span> 根据加州迪士尼为蓝本的主题乐园li>
<li>
<span>特色3span> 欣赏百老汇式精彩表演li>
<li>
<span>特色4span> 畅玩乐趣无穷的动感机动游戏li>ul>
<p>香港迪士尼乐园简介p>
<ul>
<li>香港迪士尼乐园度假区位于香港大屿山,于2005年9月开幕,为宾客提供奇妙迪士尼体验。li>
<li>香港迪士尼乐园分七大主题园区,他们是美国小镇大街、幻想世界、明日世界、探险世界、2011年11月开幕的反斗奇兵大本营、2012年7月开幕的灰熊山谷以及2013年5月开幕的迷离庄园。代表不同年代不同时空的迪士尼体验。li>ul>
<div class="jianjie">
<h4>·香港迪士尼乐园10周年庆典多个全新精彩演出的体验h4>
<font>香港迪士尼乐园踏入10周年,整个乐园将化身成为耳目一新的华丽大舞台;全新的音乐剧“迪士尼魔法书房”、崭新的影像投影展现“星梦奇缘”烟花表演、全新座落于幻想世界的“童话园林”,以及多款精美礼品、美食和更多精彩活动,诚邀您和挚爱一起投入香港迪士尼乐园10周年庆典,缔造Happily Ever After的时光。font>div>
<div class="jianjie">
<h4>·全新百老汇式音乐剧迪士尼魔法书房h4>
<font>万众期待的全新音乐剧“迪士尼魔法书房”是特别为香港而创作、乐园历年最大型的娱乐表演制作,由米奇老鼠和高飞带领宾客穿梭7个迪士尼故事,透过华丽表演与崭新舞台技术,投入一个新层次的奇妙旅程。不论音乐、编舞、服饰及舞台设计等都融入了经典及现代元素。
<br/>“迪士尼魔法书房”长达28分钟,将于幻想世界的迪士尼故事剧场上演,故事发生于一间魔法书房内,米奇老鼠与高飞好奇打开魔法书,因而令人见人爱的雪人小白从故事书中跌了出来。为了在小白溶化之前带他回到自己的故事,米奇老鼠、高飞及小白踏上奇妙的历险旅程,穿梭于7个迪士尼故事,包括:《小泰山》、《小鱼仙》、《魔发奇缘》、《勇敢传说之幻险森林》、《阿拉丁》、《公主与青蛙》及《魔雪奇缘》。
<br/>而为庆祝乐园10周年特别创作的主题歌曲《Happily Ever After》更会压轴登场,为华丽的演出带来圆满的结局。font>
<div class="anli">
<img class="left" style="float:left;" src="img/dishini.jpg">
<img class="right" style="float:right" src="img/aa.jpg">div>
div>
<div class="jianjie">
<h4>·全新百老汇式音乐剧迪士尼魔法书房h4>
<font>万众期待的全新音乐剧“迪士尼魔法书房”是特别为香港而创作、乐园历年最大型的娱乐表演制作,由米奇老鼠和高飞带领宾客穿梭7个迪士尼故事,透过华丽表演与崭新舞台技术,投入一个新层次的奇妙旅程。不论音乐、编舞、服饰及舞台设计等都融入了经典及现代元素。
<br/>“迪士尼魔法书房”长达28分钟,将于幻想世界的迪士尼故事剧场上演,故事发生于一间魔法书房内,米奇老鼠与高飞好奇打开魔法书,因而令人见人爱的雪人小白从故事书中跌了出来。为了在小白溶化之前带他回到自己的故事,米奇老鼠、高飞及小白踏上奇妙的历险旅程,穿梭于7个迪士尼故事,包括:《小泰山》、《小鱼仙》、《魔发奇缘》、《勇敢传说之幻险森林》、《阿拉丁》、《公主与青蛙》及《魔雪奇缘》。
<br/>而为庆祝乐园10周年特别创作的主题歌曲《Happily Ever After》更会压轴登场,为华丽的演出带来圆满的结局。font>
<div class="anli">
<img class="left" style="float:left;" src="img/dishini.jpg">
<img class="right" style="float:right" src="img/aa.jpg">div>
div>
div>
div>
<div class="tab_show">
<div class="knowq">div>
div>
<div class="tab_show">
<div class="know">
<div class="tupp" style="width:70%;margin:100px auto 50px auto;">
<img src="img/dengdai.png" style="width:100%;">
div>
div>
div>
section>
body>
<script type="text/javascript">
//初始化调用方法
$(function() {
var openId = '<%=session.getAttribute("openId")%>';
var headimgurl = '<%=session.getAttribute("headimgurl")%>';
var nickname = '<%=session.getAttribute("nickname")%>';
$.ajax({
//请求接口获取个人购买记录,返回code开奖编码,timeStr购买时间,status是否中奖(0未开奖,1已中奖,2未中奖)
url: "http://你的域名/weChatpay/recodeServlet?flag=1&openId=" + openId,
type: "get",
dataType: "json",
success: function(data) {
//将开奖编码循环写到页面上
$.each(data,
function(i, list) {
var str = "";
$.each(list,
function(j, item) {
//status=1表示该编码中奖,加上中奖啦图片
if (item.status == 1) {
str += "" + "" + "">" + nickname + "" + "" + item.code + "" + "" + item.timeStr + "" + "";
} else {
str += "" + "" + "">" + nickname + "" + "" + item.code + "" + "" + item.timeStr + "" + "";
}
});
$(".tab_show .knowq").html(str);
});
}
});
});
script>
<script type="text/javascript">
//点击购买按钮调用方法
$(".canyu").click(function() {
//获取进度条元素
var progress = $("progress");
//进度条不满10人时执行下面代码,若等于10为待开奖状态,则什么也不执行
if (progress.val() != 10) {
var openId = '<%=session.getAttribute("openId")%>';
//num购买数量,money总金额(单位分)
var num = $(".Amount").val();
var money = num * 100;
//防止并发超卖,购买前先增加订单数量判断是否小于10,支付成功后再发放中奖码,若支付失败再减去相应订单数量
$.ajax({
url: "http://你的域名/weChatpay/recodeServlet?flag=3&num=" + num,
type: "get",
dataType: "json",
success: function(data) {
//error=1 增加订单后总订单数小于等于10
if (data.error == 1) {
//调用支付接口
$.ajax({
url:"http://你的域名/weChatpay/topayServlet?openId="+openId+"&money="+money,
type: "get",
dataType: "json",
success: function(json) {
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId": json.appId,
"timeStamp": json.timeStamp,
"nonceStr": json.nonceStr,
"package": json.packages,
"signType": "MD5",
"paySign": json.sign
},
//调起微信支付成功
function(res) {
WeixinJSBridge.log(res.err_msg);
if (res.err_msg == "get_brand_wcpay_request:ok") {
//alert("微信支付成功!");
//跳转到生成开奖编码的servlet
window.location.href = "http://你的域名/weChatpay/resultServlet?openId=" + openId + "&num=" + num;
// }else if(res.err_msg == "get_brand_wcpay_request:cancel"){
//alert("用户取消支付!");
} else {
//支付失败或取消,恢复到原来的订单量
$.ajax({
url: "http://你的域名/weChatpay/recodeServlet?flag=4&num=" + num,
type: "get",
dataType: "json"
});
}
});
},
//调起微信支付失败,恢复到原来的订单量
error:function(){
$.ajax({
url: "http://你的域名/weChatpay/recodeServlet?flag=4&num=" + num,
type: "get",
dataType: "json"
});
}
});
//error=0 表示增加订单后总订单数大于10
} else {
alert("库存不足");
}
}
});
}
});
script>
html>
4.处理支付的接口
TopayServlet.java
package com.servlet;
import java.io.IOException;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import com.utils.GetWxOrderno;
import com.utils.RequestHandler;
import com.utils.Sha1Util;
import com.utils.TenpayUtil;
public class TopayServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
JSONObject json = new JSONObject();
//网页授权后获取传递的参数
String money = request.getParameter("money");//分为单位
String openId = request.getParameter("openId");
//金额转化为分为单位
// float sessionmoney = Float.parseFloat(money);
// String finalmoney = String.format("%.2f", sessionmoney);
// finalmoney = finalmoney.replace(".", "");
int intMoney = Integer.parseInt(money);
//商户相关资料
String appid = "";//公众号appid
String appsecret = "";//公众号秘钥
String partner = "";//商户号
String partnerkey = "";//商户API秘钥
//用于获取随机数
String currTime = TenpayUtil.getCurrTime();//获取当前时间
String strTime = currTime.substring(8, currTime.length());//8位日期
String strRandom = TenpayUtil.buildRandom(4) + "";//四位随机数
String strReq = strTime + strRandom;//10位序列号,可以自行调整
String orderNo=appid+Sha1Util.getTimeStamp();//随机生成了一个订单号
//商户号
String mch_id = partner;
//子商户号 非必输
//String sub_mch_id="";
//设备号 非必输
String device_info="WEB";
//随机数
String nonce_str = strReq;
//商品描述
String body = "一元夺宝";
//附加数据
String attach = "xx科技有限公司";
//商户订单号
String out_trade_no = orderNo;
//总金额以分为单位,不带小数点
int total_fee = intMoney;
//订单生成的机器 IP
String spbill_create_ip = request.getRemoteAddr();
//订 单 生 成 时 间 非必输
// String time_start ="";
//订单失效时间 非必输
// String time_expire = "";
//商品标记 非必输
// String goods_tag = "";
//支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等:http://*/weChatpay/notifyServlet
String notify_url ="http://你的域名/weChatpay/notifyServlet";
String trade_type = "JSAPI";
String openid = openId;
//非必输
// String product_id = "";
//获取sign(统一下单接口签名)
//第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),
//使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
//第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,
//再将得到的字符串所有字符转换为大写,得到sign值signValue。
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", appid);
packageParams.put("mch_id", mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", body);
packageParams.put("attach", attach);
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("total_fee", money);
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
packageParams.put("openid", openid);
RequestHandler reqHandler = new RequestHandler(request, response);
reqHandler.init(appid, appsecret, partnerkey);
String sign = reqHandler.createSign(packageParams);
//统一下单接口携带参数(xml格式),接口地址https://api.mch.weixin.qq.com/pay/unifiedorder
String xml="" +
"" +appid+""+
"" +mch_id+""+
"" +nonce_str+""+
"" +sign+""+
"+body+"]]>"+
"" +attach+""+
"" +out_trade_no+""+
//金额,这里写的1 分到时修改,测试用
// ""+1+" "+
"" +total_fee+""+
"" +spbill_create_ip+""+
"" +notify_url+""+
"" +trade_type+""+
"" +openid+""+
"";
System.out.println(xml);
String allParameters = "";//没用
try {
allParameters = reqHandler.genPackage(packageParams);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//请求微信统一下单接口,成功后返回预支付交易会话标识prepay_id
String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String prepay_id = "";
try {
prepay_id = new GetWxOrderno().getPayNo(createOrderURL, xml);
if(prepay_id.equals("")){
request.setAttribute("ErrorMsg", "统一支付接口获取预支付订单出错");
response.sendRedirect("error.jsp");
}
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//生成H5调起微信支付API相关参数(前端页面js的配置参数)
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
String timestamp = Sha1Util.getTimeStamp();//当前时间的时间戳
String packages = "prepay_id="+prepay_id;;//订单详情扩展字符串
finalpackage.put("appId", appid);//公众号appid
finalpackage.put("timeStamp", timestamp);
finalpackage.put("nonceStr", strReq); //随机数
finalpackage.put("package", packages);
finalpackage.put("signType", "MD5");//签名方式
String finalsign = reqHandler.createSign(finalpackage);//签名
json.put("appId", appid);
json.put("timeStamp", timestamp);
json.put("nonceStr", strReq);
json.put("packages", packages);
json.put("sign", finalsign);
response.getWriter().append(json.toString());
//response.sendRedirect("shop/index.jsp?appid="+appid+"&timeStamp="+timestamp+"&nonceStr="+strReq+"&package="+packages+"&sign="+finalsign);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
开发前最好详细阅读微信支付开发文档,理解所用接口,以及相关签名、参数等。
代码中用的其他类以及完整demo见下面链接,demo中有需要修改参数的说明文档。
源码链接