微信支付分为好几种,我要记录的是微信公众号支付和网页微信扫码支付这两种:
现在来讲一讲微信公众号支付,(网页微信扫码支付会在下一篇博客讲,这篇博客讲的是微信公众号支付):
无论是微信公众号支付还是网页微信扫码支付都要准备好以下条件,接下来的调用接口需要用到:
1.已成功认证的微信公众号(服务号),拿到公众号的AppID,AppSecret;订阅号无论是否认证很多接口都调用不了建议不要用。
看图说话:
2.在公众号上开通 商户平台,拿到商户号和app商户秘钥;
3.一个公网可以访问到的服务器和一个公网可以访问到的域名,这里不管你是买服务器买域名还是用内网穿透软件都行,只要能让公网访问到自己写的后台程序就ok。注意:公网访问不能带端口号
以上这些准备妥当之后就可以进行微信公众号支付开发了
微信支付这块第一步问题很多,走出了之后就不怎么难了;
第一步获取用户信息之后然后再跳转到支付页面:关键词openid
点击进入到支付页面的同时需要获取用户的当前基本信息其中包括openid(微信用户唯一标识,名词解释自己去微信官网了解);
当用户点击支付时让用户直接进入以下方法中,获取到用户信息后然后再重定向到页面(注意:我这里用的是ssh框架);
所以我的进入支付的链接就是进入后台方法的链接:http://域名/WeChat/WxPaycode.action?biao=1;
这个biao的参数是我个人的业务需求,是为了后续跳转用的,不需要就不加!
// 获取code 微信支付第一步
public String code() {
String redirect_uri="";
try {
//redirect_uri 是访问微信接口之后的回调地址,我这边是回调到action WxPay的openid方法去
if(biao==1){
redirect_uri = URLEncoder.encode(WeiXinUtil.ip + "WxPayopenid.action?biao=1", "utf-8");
}else if(biao==2){
redirect_uri = URLEncoder.encode(WeiXinUtil.ip + "WxPayopenid.action?biao=2", "utf-8");
}
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="
+ WeiXinUtil.appid
+ "&redirect_uri="
+ redirect_uri
+ "&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
//scope作用域,这里是获取基本信息snsapi_base,如果要获取详细信息就不一样,不同之处对照微信官方文档
//基本信息就获取openid,详细信息是获取包括openid、用户头像等等的信息
ServletActionContext.getResponse().sendRedirect(url);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
代码中的WeiXinUtil是一个实体类,主要是放一下调用微信接口常用的数据,例如公众号的appid:
WeiXinUtil.java
/**
* 微信支付用到的商家数据
* @author QT-666
*
*/
public class WeiXinUtil {
public static String appid ="*********************";//公众号应用id
public static String secret ="***********************";//应用秘钥
public static String APIid ="*****************************";//商户支付api秘钥
public static String ip = "http://域名/WeChat/";//域名或IP地址
// public static String ip = "http://域名/WeChat/";//域名或IP地址
public static String account = "******";//微信商户支付号
}
把上面的 “ * ” 号替换为自己的真实数据
紧跟第一个方法的 回调地址中的openid方法:
// 获取open_id; 第二步:由第一步自动回调到这个方法,然后在这个方法里自动重定向到支付页面
public String openid() {
HttpSession session = request.getSession();
String code = request.getParameter("code");
try {
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
+ WeiXinUtil.appid
+ "&secret="
+ WeiXinUtil.secret
+ "&code=" + code + "&grant_type=authorization_code";
if (code != null) {
String json = HttpUtils.get(requestUrl);//HttpUtils是远程访问工具类,代码后面提供
//new Gson().fromJson不知道就去百度
WechatResult result = new Gson().fromJson(json,
WechatResult.class);//WechatResult返回值接收实体类,其中就有openid属性,代码后面提供
String OPEN_ID = result.getOpenid();
String access_token = result.getAccess_token();
session.setAttribute("token", access_token);
session.setAttribute("open_id", OPEN_ID); // 存入open_id
//因为我这要保存一下支付记录,所以我这里需要保存用户信息的业务逻辑
UserInfo u = userService.findUserByOpenId(OPEN_ID);
int id=0;
if (u == null) {
UserInfo user = new UserInfo();
user.setFake(0);
user.setUser_score(200);
user.setUser_name("");
user.setUser_phone("1");
user.setOpen_id(OPEN_ID);
id = userService.addUser(user);// 添加后的主键
session.setAttribute("user", user);
}
session.setAttribute("user", u);
if(biao==1){//重定向到支付页面
ServletActionContext.getResponse().sendRedirect(
WeiXinUtil.ip + "wxpay/pay.html");
return null;
}else if(biao==2){
ServletActionContext.getResponse().sendRedirect(
WeiXinUtil.ip + "wxpay/info.jsp");
return null;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
到这里就已经获取到用户的信息了,接下来就到支付页面去了;openid已拿到!第一步完成了!!
WechatResult.java,省略了get set方法
/**
* 用户授权信息类
* @author QT-666
*
*/
public class WechatResult {
private String access_token; //网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
private int expires_in; //access_token接口调用凭证超时时间,单位(秒)
private String refresh_token; //用户刷新access_token
private String openid; //用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
private String scope; //用户授权的作用域,使用逗号(,)分隔
HttpUtils.java一般这种访问接口都会用到这种工具类,可以根据自己需求修改,网上很多,各色各样。不过我还是在这里提供一份我用的吧!
package com.game.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.Key;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
import org.apache.http.NameValuePair;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import com.game.model.Find_university;
import com.game.model.Select_info;
import com.google.gson.JsonArray;
/**
* http post 提交 和 get请求
* @author QT-666
*
*/
public class HttpUtils {
private static RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(15000).setConnectTimeout(15000)
.setConnectionRequestTimeout(15000).build();
public static void get(String url, Map<String, String> params){
CloseableHttpClient httpClient = null;
HttpGet httpGet = null;
try {
httpClient = HttpClients.createDefault();
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(20000).setConnectTimeout(20000).build();
String ps = "";
for (String pKey : params.keySet()) {
if(!"".equals(ps)){
ps = ps + "&";
}
ps = pKey+"="+params.get(pKey);
}
if(!"".equals(ps)){
url = url + "?" + ps;
}
httpGet = new HttpGet(url);
httpGet.setConfig(requestConfig);
CloseableHttpResponse response = httpClient.execute(httpGet);
HttpEntity httpEntity = response.getEntity();
System.out.println(EntityUtils.toString(httpEntity,"utf-8"));
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(httpGet!=null){
httpGet.releaseConnection();
}
if(httpClient!=null){
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 发送 post请求
* @param httpUrl 地址
* @param maps 参数
*/
public static void post(String url, Map<String, String> params){
CloseableHttpClient httpClient = null;
HttpPost httpPost = null;
try {
httpClient = HttpClients.createDefault();
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(20000).setConnectTimeout(20000).build();
httpPost = new HttpPost(url);
httpPost.setConfig(requestConfig);
List<NameValuePair> ps = new ArrayList<NameValuePair>();
for (String pKey : params.keySet()) {
ps.add(new BasicNameValuePair(pKey, params.get(pKey)));
}
httpPost.setEntity(new UrlEncodedFormEntity(ps));
CloseableHttpResponse response = httpClient.execute(httpPost);
HttpEntity httpEntity = response.getEntity();
System.out.println(EntityUtils.toString(httpEntity,"utf-8"));
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(httpPost!=null){
httpPost.releaseConnection();
}
if(httpClient!=null){
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 发送post请求Https,参数是字符串
* @param httpPost
* @return
*/
public static String post(String url, String body) throws Exception{
String str="";
CloseableHttpClient httpClient = null;
HttpPost httpPost = null;
try {
httpClient = HttpClients.createDefault();
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(20000).setConnectTimeout(20000).build();
httpPost = new HttpPost(url);
httpPost.setConfig(requestConfig);
httpPost.setEntity(new StringEntity(body,"utf-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
HttpEntity httpEntity = response.getEntity();
str = EntityUtils.toString(httpEntity,"utf-8");
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(httpPost!=null){
httpPost.releaseConnection();
}
if(httpClient!=null){
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return new String(str.getBytes("iso-8859-1"));
}
public static String get(String strURL) throws Exception{
URL url = new URL(strURL);
HttpURLConnection httpConn = (HttpURLConnection)
url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(
httpConn.getInputStream(),"utf-8"));
String line;
StringBuffer buffer = new StringBuffer();
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
reader.close();
httpConn.disconnect();
return buffer.toString();
}
//url表示请求链接,param表示json格式的请求参数 //自定义菜单创建访问方式
public static String sendPost(String url, Object param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性 注意Authorization生成
// conn.setRequestProperty("Content-Type",
// "application/x-www-form-urlencoded");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(),"utf-8"));
// 发送请求参数
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;
}
System.out.println(result);
} 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;
}
public List<Select_info> PostUrl(String url) throws Exception{
//获取查询信息URLEncoder.encode(q, "UTF-8")
//String msg = ht.get("http://域名/api/GetCollegeResult/GetRecommendResult?localAreas=陕西&score_rank=1123&areas=河北,江西,陕西&majors=预防医学(五年)&ftype=2&f_type=236");
//请求获取返回字符(中文转16进制)
String ur="http://域名/api/GetCollegeResult/GetRecommendResult?localAreas="+URLEncoder.encode("陕西","UTF-8")+"&score_rank=1123&areas="+URLEncoder.encode("河北,江西,陕西","UTF-8")+"&majors="+URLEncoder.encode("预防医学(五年)","UTF-8")+"&ftype=2&f_type=236";
//String msg = get("http://域名/api/GetCollegeResult/GetRecommendResult?localAreas=%E9%99%95%E8%A5%BF&score_rank=1123&areas=%E6%B2%B3%E5%8C%97,%E6%B1%9F%E8%A5%BF,%E9%99%95%E8%A5%BF&majors=%E9%A2%84%E9%98%B2%E5%8C%BB%E5%AD%A6(%E4%BA%94%E5%B9%B4)&ftype=2&f_type=236");
String msg = get(ur);
//信息分段截取
//1.去掉字符串第一个和最后一个
String str = msg.substring(0,msg.length()-1);
str=str.substring(1);
//数据为["3823d737-d9ed-4556-a0f5-c7a52af8bf50","XITKSTIT","南昌大学","康复治疗学","3","0","",0,"2018-06-14T23:30:49","2018-06-14T23:30:49","1","101005",null,null,545.0,14402.0],[....
//2.对以上数据进行分段,通过‘[’,']'进行分段
//过滤其他[
//使用List集合保存对象
List<Select_info>list=new ArrayList<Select_info>();
//创建对象
Select_info info=new Select_info();
String s="";
String st= str.replace("[","");
st= str.replace("\"","");
String[]split =st.split("],");
for(int i=0;i<split.length;i++){
if(i>split.length){
break;
}
for(int j=0;j<16;j++){
s=split[i].toString();
String[]spli =s.split(",");
//对象拼接
info.setId(spli[0]);
info.setF_query_termId(spli[1]);
info.setColleges(spli[2]);
info.setMajors(spli[3]);
info.setF_type(spli[4]);
info.setIsPay(spli[5]);
info.setRemark(spli[6]);
//info.setStatus(spli[7]);
info.setModified(spli[8]);
info.setCreated(spli[9]);
info.setF_typeD(spli[10]);
info.setMajors_code(spli[11]);
info.setMajors_advantage(spli[12]);
info.setMajors_evaluation(spli[13]);
//info.setMajors_score(spli[14]);
//info.setMajors_rank(spli[15]);
list.add(i, info);
if(spli[j]==null){
break;
}
}
System.out.println("获取"+list.size());
}
return list;
}
public String getUrl(String url) throws Exception{
String msg = get(url);
return msg;
}
public static void main(String[] args) throws Exception {
String code="";
String url ="http://域名/api/getcollegeresult/GetMajors?key="
+URLEncoder.encode("软件技术","UTF-8");
HttpUtils du =new HttpUtils();
code = du.getUrl(url);
System.out.println(code);
/*//获取查询信息
String msg = get("http://域名/api/GetCollegeResult/GetRecommendResult?localAreas=陕西&score_rank=1123&areas=河北,江西,陕西&majors=预防医学(五年)&ftype=2&f_type=236");
//信息分段截取
//1.去掉字符串第一个和最后一个
String str = msg.substring(0,msg.length()-1);
str=str.substring(1);
//数据为["3823d737-d9ed-4556-a0f5-c7a52af8bf50","XITKSTIT","南昌大学","康复治疗学","3","0","",0,"2018-06-14T23:30:49","2018-06-14T23:30:49","1","101005",null,null,545.0,14402.0],[....
//2.对以上数据进行分段,通过‘[’,']'进行分段
//过滤其他[
//使用List集合保存对象
List<Select_info>list=new ArrayList<Select_info>();
//List<Select_info>list2=new ArrayList<Select_info>();
int ids=3;
Select_info info=new Select_info();
String s="";
String st= str.replace("[","");
st= str.replace("\"","");
String[]split =st.split("],");
for(int i=0;i<split.length;i++){
if(i>split.length){
break;
}
for(int j=0;j<16;j++){
s=split[i].toString();
String[]spli =s.split(",");
info.setId(spli[0]);
info.setF_query_termId(spli[1]);
info.setColleges(spli[2]);
info.setMajors(spli[3]);
info.setF_type(spli[4]);
info.setIsPay(spli[5]);
info.setRemark(spli[6]);
info.setStatus(spli[7]);
info.setModified(spli[8]);
info.setCreated(spli[9]);
info.setF_typeD(spli[10]);
info.setMajors_code(spli[11]);
info.setMajors_advantage(spli[12]);
info.setMajors_evaluation(spli[13]);
info.setMajors_score(spli[14]);
info.setMajors_rank(spli[15]);
list.add(i, info);
if(spli[j]==null){
break;
}
//info.setId(spli[j]);
//System.out.println(spli[j]);
}
//
System.out.println(list.get(i).getMajors());
}
*/
//分段数据序列成数组
//String[]split =str.split(",");
//创建自定义菜单
/*String url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=7__Q8qY7aZYK13ASiiEzidF4W87d4arKZXJApUHAEsQRyrDDb8iGrtuMKIX809fikh7u1H8x-_VW0OTsIm2Ha3YsbfX4SppMPsN5kBJ10EJdqU35e7bAbJUgNLYZoIBTgAIALEW";
JSONObject menu = JSONObject.fromObject(demo.initMenu());
String msg = sendPost(url,menu);*/
//获取自定义菜单配置接口
/*String url = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=7__Q8qY7aZYK13ASiiEzidF4W87d4arKZXJApUHAEsQRyrDDb8iGrtuMKIX809fikh7u1H8x-_VW0OTsIm2Ha3YsbfX4SppMPsN5kBJ10EJdqU35e7bAbJUgNLYZoIBTgAIALEW";
String msg = get(url);*/
//System.out.println(msg);
}
}
工具类的话没什么可说的,会用就行;
第二步,支付页面上的花里胡哨环境配置!
这一步也繁琐,一点错都不能有,因为要真正开始调用微信js开放接口了,页面环境要配置完美ok才能调用微信js开放接口中的微信支付方法;
这里所说的支付页面环境配置就是wx.config里面的配置,要返回config:ok 了就万事大吉了!这里推荐个可以在电脑端就可以看到wx.config是否ok的工具,就是微信官方推出的微信web开发者工具,需要的话自己去微信官网上下载!因为这种微信接口方面的报错在微信手机端是看不到的,只有这工具能显示支付页面出到底是哪里错了!
直接上支付页面代码吧,不废话!
<!doctype html>
<html>
<head>
<title>充值</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="stylesheet" href="css/jquery-weui.min.css">
<link rel="stylesheet" href="css/weui.min.css">
<script src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js "></script>
<script src="http://pv.sohu.com/cityjson?ie=utf-8"></script>
<script src="js/jquery-2.1.4.js"></script>
<style>
.pay{ width:100%; height:150px;}
ul {
margin-left:10px;
margin-right:10px;
margin-top:10px;
padding: 0;
}
li {
width: 32%;
float: left;
margin: 0px;
margin-left:1%;
padding: 0px;
height: 60px;
display: inline;
line-height:60px;
color: #fff;
font-size:16px;
word-break:break-all;
word-wrap : break-word;
margin-bottom: 5px;
}
a {
-webkit-tap-highlight-color: rgba(0,0,0,0);
background:rgba(204,204,204,0.3)
width: 48%;
height: 60px;
text-decoration:none;
color:#000;
}
a:link{
-webkit-tap-highlight-color: rgba(0,0,0,0);
text-decoration:none;
color:#000;
}
a:visited{
-webkit-tap-highlight-color: rgba(0,0,0,0);
text-decoration:none;
color:#000;
}
a:hover{
-webkit-tap-highlight-color:rgba(0,0,0,0);
text-decoration:none;
color:#fff;
}
a:active{
-webkit-tap-highlight-color:rgba(0,0,0,0);
text-decoration:none;
}
.dan{width:90%; height:auto; margin-left:5%;}
.option-input {
-webkit-appearance: none;
width: 30px;
height: 30px;
float:right;
background: #cbd1d8;
border: none;
color: #fff;
cursor: pointer;
display: inline-block;
outline: none;
margin-right: 0.5rem;
z-index: 1000;
}
.option-input:checked {
background:#F00;
}
.option-input:checked::before {
width: 30px;
height: 30px;
position: absolute;
content: '\2714';
display: inline-block;
font-size: 26.66667px;
text-align: center;
line-height: 30px;
}
.option-input.radio {
border-radius: 50%;
}
.option-input.radio::after {
border-radius: 50%;
}
body label {
display: block;
line-height: 30px;
margin-top:20px;
}
.reminder{width:90%; height:60px; text-align:center; margin-top:100px; margin-left:5%; font-size:14px;}
.btn{ width:90%; height:40px; background:#F00; margin-left:5%; border:0; font-size:16px; color:#FFF;margin-top:40px;}
</style>
<script src="js/jquery-2.1.4.js"></script>
<script>
$(document).ready(function(){
$.ajax({
url:"WxPaygetAccess.action",
type:"post",
dataType:"json",
data:null,
async : false,//同步方式
success:function(data){
$("#a").val(data.s.timeStamp);
$("#b").val(data.s.nonceStr);
$("#c").val(data.s.signature);
}
});
});
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '************', // 必填,公众号的唯一标识,这里填写自己的公众号appid
timestamp:$("#a").val(), // 必填,生成签名的时间戳
nonceStr: $("#b").val(), // 必填,生成签名的随机串
signature: $("#c").val(),// 必填,签名,见附录1
jsApiList: ['wx.chooseWXPay'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.ready(function(){
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
</script>
<script>
$(document).ready(function(){
$(".ss1").click(function(){
$(".t1").css({"background":"#F00","display":"block","color":"#fff"});
$(".t2").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t3").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t4").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t5").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t6").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
payMoney(8);
});
$(".ss2").click(function(){
$(".t1").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t2").css({"background":"#F00","display":"block","color":"#fff"});
$(".t3").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t4").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t5").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t6").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
payMoney(88);
});
$(".ss3").click(function(){
$(".t1").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t2").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t3").css({"background":"#F00","display":"block","color":"#fff"});
$(".t4").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t5").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t6").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
payMoney(188);
});
$(".ss4").click(function(){
$(".t1").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t2").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t3").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t4").css({"background":"#F00","display":"block","color":"#fff"});
$(".t5").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t6").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
payMoney(288);
});
$(".ss5").click(function(){
$(".t1").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t2").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t3").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t5").css({"background":"#F00","display":"block","color":"#fff"});
$(".t4").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t6").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
payMoney(388);
});
$(".ss6").click(function(){
$(".t1").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t2").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t3").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t6").css({"background":"#F00","display":"block","color":"#fff"});
$(".t5").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
$(".t4").css({"background":"rgba(204,204,204,0.3)","display":"block","color":"#000"});
payMoney(488);
});
});
function payMoney(money){
$("#money").val(money);
}
function cleanMsg(){
$("#msg").html("");
}
//跳转后台支付
function pay(){
var money=$("#money").val();
var tel = $("#tel").val();
var type= $("input[name='example']:checked").val();
if(money!=""&&money!='0'&&tel!=""&&type!=""){
if(!(/^1[34578]\d{9}$/.test(tel))){ //验证手机号格式
$("#msg").html("手机号格式不正确!");
}
else{ //判断是哪种充值方式
var ip = returnCitySN["cip"];
if(type==2){//微信
$.ajax({
url:"WxPayweixinPay.action",
type:"post",
dataType:"json",
data:{num:money,ip:ip,tel:tel},
async : false,//同步方式
success:function(data){
if(data.pay==1){
$("#msg").text("请先注册会员再来充值!");
}
else if(data!=null){
var obj=data.pay;
var appId = obj.appId;
//timeStamp = new Date().getTime();
var timeStamp = obj.timeStamp;
var nonceStr = obj.nonceStr;
var prepay_id = obj.prepay_id;
var signType = obj.signType;
var paySign = obj.paySign;
wx.chooseWXPay({
timestamp: timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: nonceStr, // 支付签名随机串,不长于 32 位
package: "prepay_id="+prepay_id, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
signType: signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
paySign: paySign, // 支付签名
success: function (res) {
// 支付成功后的回调函数
window.location.href="pay_succ.html?num="+num;
}
});
}
else{
alert("支付失败");
};
},
});
}
else if(type==1){//支付宝
$.ajax({
url:"alipay_checkUser.action",
type:"post",
dataType:"json",
data:{num:money,ip:ip,tel:tel},
async : false,//同步方式
success:function(data){
if(data.pay==0){
$("#msg").text("请先注册会员再来充值!");
return false;
}
else{
var btn = document.querySelector("#pay_btn");
btn.addEventListener("click", function (e) {
//alert(type);
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
//订单号
var vNow = new Date();
var sNow = "";
sNow += String(vNow.getFullYear());
sNow += String(vNow.getMonth() + 1);
sNow += String(vNow.getDate());
sNow += String(vNow.getHours());
sNow += String(vNow.getMinutes());
sNow += String(vNow.getSeconds());
sNow += String(vNow.getMilliseconds());
var out_trade_no = sNow;
//订单名称,必填
//var subject = "农庄游戏支付";
//表单参数
var queryParam = '';
//订单号
queryParam += "out_trade_no" + "=" + encodeURIComponent(out_trade_no) + '&';
//支付金额
queryParam += "total_amount" + "=" + encodeURIComponent(money) + '&';
//电话号码
queryParam += "tel" + "=" + encodeURIComponent(tel) + '&';
//订单名称
//queryParam += "subject" + "=" + encodeURIComponent(subject) + '&';
var gotoUrl = "alipay_pay.action" + '?' + queryParam;
_AP.pay(gotoUrl);
return false;
}, false);
btn.click();
return true;
}
}
});
}
}
}
}
</script>
<script type="text/javascript" src="alipay/ap.js"></script>
</head>
<body ontouchstart="">
<input type="hidden" id="a">
<input type="hidden" id="b">
<input type="hidden" id="c">
<form action="#" method="post">
<h4>充值金额</h4>
<div class="pay" align="center">
<ul>
<li class="ss1" style="background:rgba(204,204,204,0.3)"><a class="t1" href="javascript:" >充¥8</a></li>
<li class="ss2" style="background:rgba(204,204,204,0.3)"><a class="t2" href="javascript:">充¥88</a></li>
<li class="ss3" style="background:rgba(204,204,204,0.3)"><a class="t3"href="javascript:">充¥188</a></li>
<li class="ss4" style="background:rgba(204,204,204,0.3)"><a class="t4"href="javascript:">充¥288</a></li>
<li class="ss5" style="background:rgba(204,204,204,0.3)"><a class="t5"href="javascript:">充¥388</a></li>
<li class="ss6" style="background:rgba(204,204,204,0.3)"><a class="t6"href="javascript:">充¥488</a></li>
</ul>
<input type="hidden" id="money">
</div>
<div class="dan">
<label><img src="image/pay_logo.png" />
<input type="radio" class="option-input radio" name="example" value="1">
</label>
<label><img src="image/watchar.jpg" width="114" height="40"/>
<input type="radio" class="option-input radio" name="example" value="2">
</label>
<!-- <div class="weui-cell" style="margin-top:5px;">
<div class="weui-cell__hd"><label class="weui-label" style="font-size:18px;margin-top:-1px;">手机号:</label></div>
<div class="weui-cell__bd">
<input class="weui-input" style="font-size:18px; margin-left:-30px;" type="number" id="tel" onfocus="cleanMsg()">
</div>
</div> -->
<div class="weui-cell" >
</div>
</div>
<!-- <div class="reminder">
<p style="ccc">点击充值,即表示已阅读并同意<span style=" color:#F00;">充值协议</span></p>
<p style="">王博士农庄不会以任何形势要求您输入银行账户和密码</p>
</div> -->
<label id="msg" style="color:red;text-align:center;"></label>
<input type="button" id="pay_btn" class="btn" onclick="pay()" value="去充值">
</form>
</body>
</html>
这个页面目前是我自己用的支付页面,懒得删减凑合着用吧!这里在页面上重申几个重点:
1.页面的js导入,这个在官方文档上也有说,<script src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js "></script>必须导入这个js,和一些需要的jquery。
2.大家可以看到我在页面的初始化方法中就进入了获取wx.config需要用到的参数的后台方法中,要注意的是ajax必须同步;
$(document).ready(function(){
$.ajax({
url:"WxPaygetAccess.action",
type:"post",
dataType:"json",
data:null,
async : false,//同步方式
success:function(data){
$("#a").val(data.s.timeStamp);
$("#b").val(data.s.nonceStr);
$("#c").val(data.s.signature);
}
});
});
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '*********', // 必填,公众号的唯一标识
timestamp:$("#a").val(), // 必填,生成签名的时间戳
nonceStr: $("#b").val(), // 必填,生成签名的随机串
signature: $("#c").val(),// 必填,签名,见附录1
jsApiList: ['wx.chooseWXPay'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.ready(function(){
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
WxPaygetAccess.action方法后面提供,先说一下页面注意点,在wx.config({})中的参数属性名是固定写法,敲黑板!!是属性名不是属性值;着重注意timestamp:生成签名的时间戳;nonceStr:生成签名的随机串;signature:签名;看清楚这三个舒心名,“驼峰”都知道吧,不能错,有驼峰的给驼峰,没有的就别瞎加!不然错都不知道怎么错的。至于这三个属性的值是哪来的,这个是在页面初始化方法中生成好的,也就是后台WxPaygetAccess.action方法生成的,怎么生成的我会在后面给到代码。
点击支付按钮就调用支付方法,支付按钮方法在页面上找的到,我是经过一定的业务逻辑的,没业务需求的话可以直接就调用wx.chooseWXPay({})支付接口就好了,同样里面属性名不能错,如果一切ok的话就会弹出一个梦寐以求思念良久的微信输入支付密码弹出框了。
第三步,支付页面初始化时进来这里,获取access_token,然后根据它调用接口获取参数来配置页面支付时的环境!
接下来就是说说如何生成签名啊,随机字符串啊这些东西了,不废话,代码在哪里?
就在下面 ↓↓↓↓↓
// 第三步 支付页面初始化时进来这里,获取access_token,然后根据它调用接口获取参数来配置页面支付时的环境
public String getAccess() throws Exception {
// HttpSession session = request.getSession();
List<ToKen> list = tokenService.findToken();
Gson gson = new Gson();
Signature s = new Signature();
String ticket = "";
String access_token = "";
ToKen token = new ToKen();
if (list.size() > 0) { // 数据库有token和ticket
token = list.get(0);
long end_time = Long.parseLong(token.getEnd_time());
Date date = new Date();
long nowTime = date.getTime() / 1000;
if ((nowTime - end_time) > 6900) { // 快过期 重新获取token和ticket 并删除原来的
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
+ WeiXinUtil.appid + "&secret=" + WeiXinUtil.secret;
access_token = HttpUtils.get(url);
access_token = gson.fromJson(access_token, AccessToken.class)
.getAccess_token();
String url1 = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="
+ access_token + "&type=jsapi";
ticket = HttpUtils.get(url1);
ticket = gson.fromJson(ticket, Ticket.class).getTicket();
token.setAccess_token(access_token);
token.setTicket(ticket);
token.setEnd_time((new Date().getTime() / 1000 + 6900) + "");
tokenService.updateToken(token);
} else {// 使用原来的
access_token = token.getAccess_token();
ticket = token.getTicket();
}
} else {// 第一次获取
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
+ WeiXinUtil.appid + "&secret=" + WeiXinUtil.secret;
access_token = HttpUtils.get(url);
access_token = gson.fromJson(access_token, AccessToken.class)
.getAccess_token();
String url1 = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="
+ access_token + "&type=jsapi";
ticket = HttpUtils.get(url1);
ticket = gson.fromJson(ticket, Ticket.class).getTicket();
token = new ToKen();
token.setAccess_token(access_token);
token.setTicket(ticket);
token.setEnd_time((new Date().getTime() / 1000 + 6900) + "");
tokenService.addToken(token);
}
s.setJsapi_ticket(ticket);
s.setNoncestr(PayWxUtil.getNonceStr());//PayWxUtil.getNonceStr()工具类生成随机字符串,这个随机字符串生成有特定规则
s.setTimeStamp(new Date().getTime() / 1000 + "");//时间戳
s.setUrl(WeiXinUtil.ip + "wxpay/pay.html");//把项目中支付页面的全路劲也需要放进去
Map<String, String> map = new HashMap<String, String>();
map.put("noncestr", s.getNoncestr());
map.put("jsapi_ticket", s.getJsapi_ticket());
map.put("timestamp", s.getTimeStamp());
map.put("url", s.getUrl());
String str = "jsapi_ticket=" + s.getJsapi_ticket() + "&noncestr="
+ s.getNoncestr() + "×tamp=" + s.getTimeStamp() + "&url="
+ s.getUrl();
s.setSignature(PayWxUtil.getSha1(str));//PayWxUtil.getSha1(str)工具类真正生成签名,这里面很严格,一点都不能含糊,不然生成的签名在config中就是错的
JSONObject json = new JSONObject();
json.put("s", s);
ResUtil.write(json, ServletActionContext.getResponse());
return null;
}
token:微信关键字,理解为调用接口的凭证吧;解释在微信官方文档;通过appid和秘钥获取,规定两个小时时效性,过了两个小时需的重新调用接口获取,但是又不能一直获取,有获取次数限制。所以获取了之后会存在数据库中,并且记录该次获取之间,以便下次用时判断是否有两小时了,有的话重新获取一下,在删除数据库的存入新的token,每次重复如此。
上面这个方法是前端支付页面初始化获取签名和随机字符串以及时间戳的接口方法,所以生成好了会返回值给前端ajax的请求!
凭证token问题解决了之后就是调用工具类正式生成前端支付页面 wx.config需要的签名和随机字符串了,这里贴一下相关代码:
Token.java(凭证实体类,get set方法省略)
/**
* token 和
* @author QT-666
*
*/
public class ToKen {
private int token_id;
private String access_token;
private String ticket;
private String end_time;//生成时间
Signature.java(生成签名需要以下参数,定义好了,传入工具类中)
/**
* 生成签名实体类
* @author QT-666
*
*/
public class Signature {
private String noncestr;//随机字符串
private String jsapi_ticket;//调用js凭证
private String timeStamp;//时间戳
private String url;//支付页面地址
private String signature;//签名
最最重要的生成签名PayWxUtil.java工具类代码,没特殊情况一般不要改:
package com.game.util;
import java.security.MessageDigest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.apache.commons.codec.digest.DigestUtils;
/**
* Created by Song on 2016/11/8.
* mail: 1147649695@qq.com
* 微信支付相关工具类
*/
public class PayWxUtil {
//上一次订单请求日期
private static Date preDay = new Date();
//当前订单日期
private static Date curDay = new Date();
//用于记录已产生的订单号
private static Set<Long> numPoul = new HashSet<Long>();
/**
* 获得签名
* @param params 待编码参数,参数值为空不传入
* @param key key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
* @return
*/
public static String getSign(Map<String,String> params,String key) throws Exception{
List<String> list = new ArrayList<String>(params.keySet());
Collections.sort(list,new DictionaryCompare());
StringBuffer sb = new StringBuffer();
for(String keyVal:list){
if(params.get(keyVal)!=null){
sb.append(keyVal+"="+params.get(keyVal)+"&");
}
}
sb.append("key="+key);
return DigestUtils.md5Hex(new String(sb.toString().getBytes(),"utf-8")).toUpperCase();
}
/**
* 获得随机字符串
* @return
*/
public static String getNonceStr(){
Random random = new Random();
long val = random.nextLong();
String res = DigestUtils.md5Hex(val+"yzx").toUpperCase();
if(32<res.length()) return res.substring(0,32);
else return res;
}
/**
* 获取订单号
* 商户订单号(每个订单号必须唯一)
* 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。
* @param mchId
* @return
*/
public static String getMchBillno(String mchId){
Random random = new Random();
long val = random.nextLong()%10000000000L;//获得0-9999999999内的数字
curDay = new Date();
//隔天清空
if(curDay.after(preDay)) numPoul.clear();
while(numPoul.contains(val)){
val = random.nextLong()%10000000000L;
}
numPoul.add(val);
preDay = curDay;
//按要求,日期格式化输出
DateFormat df = new SimpleDateFormat("yyyymmdd");
return mchId+df.format(curDay)+format(val+"",10);
}
/**
* 将字符串str按长度在前面添0补齐
* @param str
* @param length
* @return
*/
private static String format(String str,int length){
String pre = "0000000000";
int len = str.length();
if(10<=len) return str.substring(0,10);
else return pre.substring(0,10-len).concat(str);
}
//SHA1加密方法,jssdk签名算法
public static String getSha1(String str){
if(str==null||str.length()==0){
return null;
}
char hexDigits[] = {'0','1','2','3','4','5','6','7','8','9',
'a','b','c','d','e','f'};
try {
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char buf[] = new char[j*2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
} catch (Exception e) {
// TODO: handle exception
return null;
}
}
}
/**
* 按字典序排序
*/
class DictionaryCompare implements Comparator<String>{
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}
工具类没什么说的,知道怎么用就ok!
后台生成好了这几个参数就给前端用了,如果config报错了,多有是签名错误,也是在这一步上出了问题,仔细检查你生成签名传的值对不对!还有就前端属性名的“驼峰”问题。config:ok了就会弹出微信支付密码输入框了,到了这一步,那恭喜你!这时就已经成功了大半;接下来就是一些支付成功或者支付失败的业务逻辑处理了,可以松一口气了!!!
第四步 统一下单生成 ,页面传入用户输入金额,ip,生成预支付订单,把微信回调的参数传递给页面,页面就可以调起支付
大家可以翻上去看看,我在支付页面上的点击按钮时,会通过ajax进入到后台方法WxPayweixinPay.action中生成调用wx.chooseWXPay({})微信支付接口的参数值;别看这里个wx.chooseWXPay({})接口里面的属性名和wx.config({})接口的差不多,但是就是不同,需要后台方法WxPayweixinPay.action重新生成给前端用,直接上WxPayweixinPay.action的方法吧:
// 统一下单生成 第四步,页面传入用户输入金额,ip,生成预支付订单,把微信回调的参数传递给页面,页面就可以调起支付
public String weixinPay() throws Exception {
HttpSession session = request.getSession();
String open_id = (String) session.getAttribute("open_id");
UserInfo u = userService.findUserByOpenId(open_id);// 查找是不是会员,不是会员不能充值
if (u != null&&u.getFake()=='T') {
String ip = request.getParameter("ip");
String token = (String) session.getAttribute("token");
String nickname = token;
double money = Double.parseDouble(request.getParameter("num"));
int a = (int) (money * 100);
WechatUnifiedOrder w = new WechatUnifiedOrder();
w.setAppid(WeiXinUtil.appid);
String str = "0791游戏-充值";
byte[] jiema = str.getBytes("utf-8"); // 解码
String bianma = new String(jiema);// 编码 如果上面的解码不对 可能出现问题
URLEncoder.encode(bianma, "UTF-8");
byte[] jiema1 = nickname.getBytes("utf-8"); // 解码
String bianma1 = new String(jiema1);// 编码 如果上面的解码不对 可能出现问题
URLEncoder.encode(bianma1, "UTF-8");
w.setAttach(bianma1);
w.setBody(bianma);
w.setMch_id(WeiXinUtil.account);
w.setNonce_str(PayWxUtil.getNonceStr());// 随机支付串
w.setNotify_url(WeiXinUtil.ip + "WxPaypayResulet.action");// 支付结果回调地址
w.setOpenid(open_id);
w.setOut_trade_no("" + new Date().getTime());
w.setSpbill_create_ip(ip);
w.setTotal_fee(a);
w.setTrade_type("JSAPI");
Map<String, String> params = new HashMap<String, String>();
params.put("attach", w.getAttach());
params.put("appid", w.getAppid());
params.put("mch_id", w.getMch_id());
params.put("nonce_str", w.getNonce_str());
params.put("body", w.getBody());
params.put("out_trade_no", w.getOut_trade_no());
params.put("total_fee", w.getTotal_fee() + "");
params.put("spbill_create_ip", w.getSpbill_create_ip());
params.put("notify_url", w.getNotify_url());
params.put("trade_type", w.getTrade_type());
params.put("openid", w.getOpenid());
w.setSign(PayWxUtil.getSign(params, WeiXinUtil.APIid));
params.put("sign", w.getSign());
// /System.out.println(w.getSign());
// 将java对象转换为XML字符串
// JaxbUtil requestBinder = new JaxbUtil(WechatUnifiedOrder.class,
// CollectionWrapper.class);
// String retXml = requestBinder.toXml(w, "utf-8");
// retXml = retXml.substring(56);
String retXml = JaxbUtil.getRequestXml(params);
String msg = HttpUtils.post(
"https://api.mch.weixin.qq.com/pay/unifiedorder", retXml);
if (msg.indexOf("FAIL") > -1) {
return null;
// JSONObject json = new JSONObject();
// json.put("pay", "error");
// ResponseUtil.write(json, ServletActionContext.getResponse());
} else {
JaxbUtil requestBinder = new JaxbUtil(TongYiReturn.class,
CollectionWrapper.class);
TongYiReturn to = requestBinder.fromXml(msg);
if (to.getReturn_code().equals("SUCCESS")
&& to.getResult_code().equals("SUCCESS")) {
EndPay pay = new EndPay();
pay.setAppId(WeiXinUtil.appid);
pay.setSignType("MD5");
pay.setTimeStamp(System.currentTimeMillis() / 1000 + "");
pay.setPrepay_id(to.getPrepay_id());
pay.setNonceStr(PayWxUtil.getNonceStr());
Map<String, String> requestMap = new HashMap<String, String>();
requestMap.put("appId", pay.getAppId());
requestMap.put("timeStamp", pay.getTimeStamp());
requestMap.put("nonceStr", pay.getNonceStr());
requestMap.put("package", "prepay_id=" + pay.getPrepay_id());
requestMap.put("signType", "MD5");
pay.setPaySign(PayWxUtil.getSign(requestMap, WeiXinUtil.APIid));
// requestMap.put("sign",pay.getPaySign());
// String ret = JaxbUtil.getRequestXml(requestMap);
// System.out.println(ret);
JSONObject json = new JSONObject();
json.put("pay", pay);
if (session.getAttribute("p_biao") != null) { // 说明时从打转盘页面进来的,支付完成后继续调到转盘页面
json.put("p_biao", 1);
session.removeAttribute("p_biao");
} else {
json.put("p_biao", 0);
}
ResUtil.write(json, ServletActionContext.getResponse());
}
}
} else { // 说明不是会员
JSONObject json = new JSONObject();
json.put("pay", 1);
ResUtil.write(json, ServletActionContext.getResponse());
}
return null;
}
这其中,前端页面传进来的支付金额money要乘以100,因为支付接口传进去的金额是按分算的,不要出现小数点;
我这里定义了支付后的回调地址,也就是告诉微信方支付之后把支付结果返回到哪里的地址,我这里写的是:
w.setNotify_url(WeiXinUtil.ip + "WxPaypayResulet.action");// 支付结果回调地址
也就是所谓的第五步了,接收支付结果并处理支付结果!第五步后面献上!!
没什么可多说的,按照格式写代码!奉上相关代码:
WechatUnifiedOrder.java
/**
* 统一下单实体类
* @author QT-666
*
*/
@XmlRootElement(name="xml")
public class WechatUnifiedOrder implements Serializable{
private String appid; //微信支付分配的公众账号ID(企业号corpid即为此appId)
private String mch_id; //微信支付分配的商户号
private String nonce_str; //随机字符串,长度要求在32位以内。推荐随机数生成算法
private String sign; //通过签名算法计算得出的签名值,详见签名生成算法
private String body; //商品简单描述,该字段请按照规范传递,具体请见参数规定
private String out_trade_no; // 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。详见商户订单号
private int total_fee; //订单总金额,单位为分,详见支付金额
private String spbill_create_ip; //APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
private String notify_url; //异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
private String trade_type; //取值如下:JSAPI,NATIVE,APP等,说明详见参数规定
private String openid; //trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识
private String attach;//数据包
同样,此方法生成好调用wx.chooseWXPay({})接口需要的参数是,返回前端支付页面使用就ok了;
支付页面上的 wx.chooseWXPay({})接口方法中也有支付回调函数,那可以作为支付成功后的跳转相应的成功或者失败页面中去;只要的还是后台接收支付结果的方法里处理并验证支付结果的后台方法;
第五步:支付页面用户输完密码后微信会把支付结果回调到这里,我们根据需要存储支付记录,和执行不同的方法
// 第五步:支付页面用户输完密码后微信会把支付结果回调到这里,我们根据需要存储支付记录,和执行不同的方法
public String payResulet() throws Exception { // 微信支付结果通知
BufferedReader reader = null;
reader = request.getReader();
String line = "";
String xmlString = null;
StringBuffer inputString = new StringBuffer();
while ((line = reader.readLine()) != null) {
inputString.append(line);
}
xmlString = inputString.toString();
request.getReader().close();
JaxbUtil requestBinder = new JaxbUtil(PayResult.class, PayResult.class);
PayResult result = requestBinder.fromXml(xmlString);
if (result.getResult_code().equals("SUCCESS")) { // 交易成功,支付结果转换为对象
String account = result.getTransaction_id();
Pay pay = payService.findPayByNo(account);
if (pay != null) { // 有交易记录 发送成功消息给商家
String returnMsg = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
HttpServletResponse response = ServletActionContext
.getResponse();
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println(returnMsg);
out.flush();
out.close();
} else { // 交易记录开始记录
Map<String, String> params = new HashMap<String, String>();
byte[] jiema1 = result.getAttach().getBytes("utf-8"); // 解码
String bianma1 = new String(jiema1);// 编码 如果上面的解码不对 可能出现问题
URLEncoder.encode(bianma1, "UTF-8");
params.put("appid", result.getAppid());
params.put("attach", bianma1);
params.put("bank_type", result.getBank_type());
params.put("fee_type", result.getFee_type());
params.put("is_subscribe", result.getIs_subscribe());
params.put("mch_id", result.getMch_id());
params.put("nonce_str", result.getNonce_str());
params.put("openid", result.getOpenid());
params.put("out_trade_no", result.getOut_trade_no());
params.put("result_code", result.getResult_code());
params.put("return_code", result.getReturn_code());
params.put("sub_mch_id", result.getSub_mch_id());
params.put("time_end", result.getTime_end());
params.put("total_fee", result.getTotal_fee() + "");
params.put("trade_type", result.getTrade_type());
params.put("transaction_id", result.getTransaction_id());
params.put("cash_fee", result.getCash_fee() + "");
params.put("device_info", result.getDevice_info());
params.put("sign_type", result.getSign_type());
params.put("settlement_total_fee",
result.getSettlement_total_fee());
params.put("cash_fee_type", result.getCash_fee_type());
params.put("coupon_fee", result.getCoupon_fee());
params.put("coupon_count", result.getCoupon_count());
params.put("coupon_type_$n", result.getCoupon_type_$n());
params.put("coupon_id_$n", result.getCoupon_id_$n());
params.put("coupon_fee_$n", result.getCoupon_fee_$n());
String sign = PayWxUtil.getSign(params, WeiXinUtil.APIid);
params.put("sign", sign);
if (sign.equals(result.getSign())) { // 两次签名一样,说明没有第三方修改,交易真实
// String token = result.getAttach();
// String[] l = msg.split(",");
// String token = l[0];// 获取用户信息凭证
// String url =
// "https://api.weixin.qq.com/sns/userinfo?access_token="
// + token
// + "&openid="
// + result.getOpenid()
// + "&lang=zh_CN";
// String json1 = HttpUtils.get(url);
// UserMsg user = new Gson().fromJson(json1, UserMsg.class);
// // 获得交易支付成功用户的信息
Pay pay1 = new Pay();
UserInfo u = userService.findUserByOpenId(result
.getOpenid()); // 查找充值的会员信息
pay1.setPay_money((double) result.getTotal_fee() / 100);
pay1.setUser_name(u.getUser_name());
pay1.setPay_no(new String(account.getBytes("utf-8")));
SimpleDateFormat simp = new SimpleDateFormat(
"yyyyMMddHHmmss");
SimpleDateFormat simp1 = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
Date date = simp.parse(result.getTime_end());
String time = simp1.format(date);
pay1.setPay_time(time); // 支付结束时间
pay1.setPay_type(1);
pay1.setUser_id(u.getUser_id());
pay1.setOpen_id(result.getOpenid());
payService.addPay(pay1); // 添加交易信息
userService.update((double) result.getTotal_fee() / 10,
u.getUser_id());
String returnMsg = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
HttpServletResponse response = ServletActionContext
.getResponse();
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println(returnMsg);
out.flush();
out.close(); // 发送成功消息给商家
}
}
}
return null;
}
相关代码:
PayResult.java
/**
* 微信支付结果返回
*
* @author QT-666
*
*/
@XmlRootElement(name="xml")
public class PayResult {
private String appid;// 微信分配的公众账号ID(企业号corpid即为此appId)
private String attach;//
private String bank_type;// 银行类型,采用字符串类型的银行标识,银行类型见银行列表
private String fee_type;// 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
private String is_subscribe;// 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
private String mch_id;// 微信支付分配的商户号
private String nonce_str;// 随机字符串,不长于32位
private String openid;// 用户在商户appid下的唯一标识
private String out_trade_no;// 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@
// ,且在同一个商户号下唯一。
private String result_code;// SUCCESS/FAIL
private String return_code;// 此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
private String sign;// 签名,详见签名算法
private String sub_mch_id;//
private String time_end;// 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
private int total_fee;// 订单总金额,单位为分
private String trade_type;// JSAPI、NATIVE、APP
private String transaction_id;// 微信支付订单号
private int cash_fee;// 现金金额
private String device_info; //微信支付分配的终端设备号,
private String sign_type;//签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
private String settlement_total_fee ;//应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
private String cash_fee_type;//货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
private String coupon_fee;//代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额
private String coupon_count;//代金券使用数量
private String coupon_type_$n;// CASH--充值代金券 NO_CASH---非充值代金券 仅在使用了免充值代金券时有返回(取值:CASH、NO_CASH)。$n为下标,从0开始编号,举例:coupon_type_0
private String coupon_id_$n;//代金券ID,$n为下标,从0开始编号
private String coupon_fee_$n;//单个代金券支付金额,$n为下标,从0开始编号
我这边是执行了相应的业务需求,保存了支付记录!保存支付记录的时候注意,因为在微信那边会多次回调这个接口,所以要判断后台存不存在这条支付记录,如果存在了就不保存支付记录了,防止保存多条一样的支付记录;
我这里时做了最简单的支付安全验证,就是校验我们生成的签名和微信给我们的签名是否一样,一样就表示没问题的支付,反之则就是有问题(可能被人串改的支付结果)。这个检验有个漏洞:就是在 微信现金红包,在用户输入金额后,点击支付时输入的金额会自动减去微信现金红包的金额,这样就可能造成两次的金额不一样,生成的签名就不同了,支付是支付了,就是后台校验的时候过不去;目前这个问题我还没解决,所以如果没什么安全要求的话可以去掉这一步的检验。
做微信支付开发,还是用内网穿透软件比较好,因为一路走下来可能会遇到各种各样的问题,都能在本地上找到并修复!比直接在服务器上开发测试好不知道多少;内网穿透软件有花生壳,还有些别的在网上可以找到;
我的框架是java三大框架ssh,struts+spring+hibernate,所以在一些接口的写法都是类似WxPayweixinPay.action,WxPay是action名字,weixinPay是这个action中的方法名;这个都应该看得懂!!
到这里就完成微信公众号的整个支付流程了,你走下来了吗!!!走下来的同学恭喜了,没走下来的同学加油,不要气馁不要急躁慢慢来会成功的!!!
学会了微信支付,相对于一些微信其他的开发也就差不多都会了,微信的很多js开放接口都是基于wx.config({})上的,只要你这个config:ok了,那别的微信开发那都不是事,恭喜你掌握了微信二次开发!
看着这篇博客写的也蛮长的,其实也就代码多,我也没废话多少;就到这里吧,有空再献上微信网页支付的博客吧;