java微信支付開發-公眾號支付
微信支付分為好幾種,我要記錄的是微信公眾號支付和網頁微信掃碼支付這兩種:
現在來講一講微信公眾號支付,(網頁微信掃碼支付會在下一篇部落格講,這篇部落格講的是微信公眾號支付):
無論是微信公眾號支付還是網頁微信掃碼支付都要準備好以下條件,接下來的呼叫介面需要用到:
1.已成功認證的微信公眾號(服務號),拿到公眾號的AppID,AppSecret;訂閱號無論是否認證很多介面都調用不了建議不要用。
看圖說話:
2.在公眾號上開通 商戶平臺,拿到商戶號和app商戶祕鑰;
3.一個公網可以訪問到的伺服器和一個公網可以訪問到的域名,這裡不管你是買伺服器買域名還是用內網穿透軟體都行,只要能讓公網訪問到自己寫的後臺程式就ok。注意:公網訪問不能帶埠號
以上這些準備妥當之後就可以進行微信公眾號支付開發了
微信支付這塊第一步問題很多,走出了之後就不怎麼難了;
第一步獲取使用者資訊之後然後再跳轉到支付頁面:關鍵詞openid
點選進入到支付頁面的同時需要獲取使用者的當前基本資訊其中包括openid(微信使用者唯一標識,名詞解釋自己去微信官網瞭解);
當用戶點選支付時讓使用者直接進入以下方法中,獲取到使用者資訊後然後再重定向到頁面(注意:我這裡用的是ssh框架);
這個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: [email protected]
* 微信支付相關工具類
*/
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 ;//應結訂單金額=訂單金額-非充值代金券金額,應