當時公司告訴我作微信公衆號支付的需求,我的根據微信支付官網以及網上的demo來作完成支付功能。微信公衆號支付分爲如下步驟:php
第一步:配置 css
1.公司需註冊微信公衆號支付,提供給後臺開發人員如下字段:html
appid:微信公衆號id==登錄微信公衆號後臺-開發-基本配置前端
secret:微信公衆號密鑰idjava
mch_id:微信支付商戶號==登錄微信支付後臺,便可看到node
key:商戶號對應的密鑰web
2.設置支付目錄:(具體步驟如微信支付官網:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3) 需在商戶平臺設置受權域名:xx.xxx.cn (注:這個域名至關於Java項目運行時的ip:端口號) 另外在商戶平臺設置支付目錄:http://域名//項目名算法
3.測試環境:測試必須在服務器上測試,能夠根據微信公衆平臺文檔下載「微信開發者工具」如:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455784140apache
首先:公司公衆號登陸管理後臺,邀請開發者微信號爲好友,同時開發者的微信要確認。json
而後:在微信開發者工具上測試看測試條件是否知足:
(1)用開發者微信登陸微信開發者工具。
(2)在瀏覽器上瀏覽獲取code的連接: https://open.weixin.qq.com/connect/oauth2/authorize? appid=wxxxxxxxxxxxxxxx&redirect_uri=http://域名/項目名/獲取openid的接口名&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect ,若環境成功,瀏覽器上會自動跳到重定向(redirect_uri)的地址上,並自帶code和state的值如:http://域名/項目名/獲取openid的接口名&code=06jjjjjxxxxxxxxxxxxxxF1q2wmF1b6jmx&state=STATE 這時開發者的測試環境成功。注:redirect_uri地址能夠進行urlEncode編碼也能夠不進行編碼。
第二步(後臺開發代碼編寫):
1.獲取openid的接口:
要獲取openid先獲取code,而code值需在前端獲取,前端瀏覽獲取code的微信連接:https://open.weixin.qq.com/connect/oauth2/authorize?
前端代碼:index.jsp以下:
<html>
<head>
<meta charset="UTF-8">
<meta name="x5-orientation" content="portrait">
<link rel="stylesheet" href="/flowsweb/css/weui.min.css">
<title>微信公衆號支付測試</title>
</head>
<body>
<div class="container" id="container">
<a href=" https://open.weixin.qq.com/connect/oauth2/authorize? appid=wxxxxxxxxxxxxxxx&redirect_uri=http://域名/項目名/獲取openid的接口名&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect " class="weui-btn weui-btn_primary" style="font-size:40px; 100px;">
當即支付
</a>
</div>
</body>
</html>
獲取openid接口代碼:
public class wxPayOpenId
{
public static Log LOG = LogFactory.getLog(wxPayOpenId.class);
private static String APPID = "wxxxxxxxxxxxxxxxxxxxx51"; //微信公衆號id
private static String SECRET = "2xxxxxxxxxxxxxxxxxc"; //微信公衆號密鑰id
private static String MCHID = "1xxxxxxxxx"; //微信支付商戶號
private static String KEY = "xxxxxxxxxxxxxxxxxxxxx"; //商戶號對應的密鑰
// private static String CODEURL = "https://open.weixin.qq.com/connect/oauth2/authorize?"; //前端獲取code的微信連接
private static String OPENIDURL = "https://api.weixin.qq.com/sns/oauth2/access_token?"; //獲取微信openid的連接
private static String UNURL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //微信統一下單連接
private static String TRADETYPE="JSAPI";//jsapi表明公衆號支付
public String execute(Context context) throws BPException {
HttpServletRequest request = (HttpServletRequest) context.getRequest();
//獲取用戶的code
String code = (String) context.getValue("code"); // 從前端請求獲取code, 針對你的框架 String code = request.getParameter("code");
System.out.println("獲得用戶code值");
System.out.println(code);
//獲取openid
String openId=getOpenId(code); //要把openid放在緩存裏,由於「微信統一下單接口」須要用openid
context.setValue("openId", String.valueOf(openId));
return "0";
}
// 獲取openId
public String getOpenId(String code) {
System.out.println("此時code不爲空");
String openIdUrl = OPENIDURL + "appid=" + APPID + "&secret=" + SECRET + "&code=" + code + "&grant_type=authorization_code";
try {
HttpRequest request = HttpRequest.post(openIdUrl).contentType("application/json;charset=utf-8");
String res = request.body();
if (res != null) {
JSONObject obj = JSON.parseObject(res);
System.out.println("輸出調用獲取openid連接的值~~~~~~~");
System.out.println(obj);
String access_token=obj.getString("access_token");
System.out.println("輸出access_token的值~~~~~~~");
System.out.println(access_token);
return obj.getString("openid");
}
return null;
}catch (Exception e) {
return null;
}
return null;
}
}
2.微信統一下單接口:
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.blade.kit.http.HttpRequest;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import sun.util.logging.resources.logging;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
public class wxPayHttp{
private static String APPID = "wxxxxxxxxxxxxxxxxxxxx51"; //微信公衆號id
private static String SECRET = "2xxxxxxxxxxxxxxxxxc"; //微信公衆號密鑰id
private static String MCHID = "1xxxxxxxxx"; //微信支付商戶號
private static String KEY = "xxxxxxxxxxxxxxxxxxxxx"; //商戶號對應的密鑰
// private static String CODEURL = "https://open.weixin.qq.com/connect/oauth2/authorize?"; //前端獲取code的微信連接
private static String OPENIDURL = "https://api.weixin.qq.com/sns/oauth2/access_token?"; //獲取openid的連接
private static String UNURL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //微信統一下單連接
private static String TRADETYPE="JSAPI";//jsapi表明公衆號支付
public String execute(Context context) throws BPException {
HttpServletRequest request = (HttpServletRequest) context.getRequest();
String money=(String) context.getValue("money");
String body=(String) context.getValue("body");
String openid=(String) context.getValue("openid"); //openid要從緩存中獲取
String xml=getxml(openid,money,body,request);
System.out.println("輸出進行簽名的數據xml:~~~~~~~~~~~~~~~");
System.out.println(xml);
String resxml=unifiedorder(xml);//解析xml字符串,取出preparepayid
System.out.println("輸出調用微信統一下單所返回的數據resxml:~~~~~~~~~~~~~~~~~~");
System.out.println(resxml); try {
Map<String, String> resultMap=WXPayUtil.xmlToMap(resxml); //WXPayUtil類將在下面展現出來
String return_code=resultMap.get("return_code");
if ("SUCCESS".equalsIgnoreCase(return_code)) {
String prepay_id= resultMap.get("prepay_id");
System.out.println("輸出prepay_id的值");
System.out.println(prepay_id);
String nonce_str= System.currentTimeMillis()+""+((int)(Math.random()*90000)+10000);//隨機生成18位數字
SortedMap<Object, Object> finalpackage = new TreeMap<Object, Object>();
String timestamp = String.valueOf(System.currentTimeMillis() / 1000); //時間戳
String packages = "prepay_id="+prepay_id; finalpackage.put("appId", APPID);
finalpackage.put("timeStamp", timestamp);
finalpackage.put("nonceStr", nonce_str);
finalpackage.put("package", packages);
finalpackage.put("signType", "MD5");
String finalsign=WXPayUtil.creatSign(finalpackage,KEY); //進行簽名
CollList data=(CollList)context.get("data");
Entity entity= new Entity(); //把以下數據放在集合data裏傳給前端
entity.addElement("appId", APPID);
entity.addElement("timeStamp",timestamp);
entity.addElement("nonceStr", nonce_str);
entity.addElement("package",packages );
entity.addElement("paySign",finalsign );
entity.addElement("signType","MD5");
//entity.addElement("success","ok" );
data.addEntity(entity);
} else {
return "-1";
}
} catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return "-1"; }
return "0";
}
//組裝統一下訂單參數並進行簽名
public static String getxml(String openid,String money,String body,HttpServletRequest request){
System.out.println("對訂單參數進行簽名+++++++++");
String returnXml = null;
try {
String nonce_str= System.currentTimeMillis()+""+((int)(Math.random()*90000)+10000);//隨機生成18位數字
String notify_url="http://域名/項目名/回調時的接口名";//微信處理完統一訂單後回調的接口url
String out_trade_no =WXPayUtil.getCurrTime()+WXPayUtil.getRandomString2(5); //商戶訂單號
int total_fee=(int)(Double.parseDouble(money)*100); //注意費用total_fee必定要是int型,微信支付官網規定的
SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
parameters.put("appid", APPID);
parameters.put("mch_id", MCHID);
parameters.put("nonce_str", nonce_str);
//parameters.put("attach", "微信支付");
parameters.put("body", body);
parameters.put("out_trade_no", out_trade_no);
parameters.put("total_fee", total_fee); //注意費用total_fee必定要是int型,微信支付官網規定的
parameters.put("spbill_create_ip", request.getRemoteAddr());//獲取訂單生成的機器IP
parameters.put("notify_url", notify_url);
parameters.put("trade_type",TRADETYPE );
parameters.put("openid", openid);
//parameters.put("device_info", "WEB");
String sign=WXPayUtil.creatSign(parameters,KEY);
returnXml="<xml>"+
"<appid>"+ APPID+"</appid>"+
"<mch_id>"+ MCHID+"</mch_id>"+
"<nonce_str>"+nonce_str+"</nonce_str>"+
"<sign>"+sign+"</sign>"+
"<body>"+body+"</body>"+
"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
"<total_fee>"+total_fee+"</total_fee>"+
"<spbill_create_ip>"+request.getRemoteAddr()+"</spbill_create_ip>"+
"<notify_url>"+notify_url+"</notify_url>"+
"<trade_type>"+TRADETYPE+"</trade_type>"+
"<openid>"+openid+"</openid>"+
"</xml>";
return returnXml;
} catch (Exception e) { return returnXml; }
}
//調用微信統一下單接口
public static String unifiedorder(String xml){
System.out.println("調用微信統一下單接口+++++++++unifiedorder");
String returnxml=null;
try {
HttpRequest request = HttpRequest.post(UNURL).contentType( "application/json;charset=utf-8").send(xml);
returnxml=request.body();
System.out.println("微信統一下單接口返回值");
System.out.println(returnxml);
return returnxml;
} catch (Exception e) { return returnxml;}
}
}
3.微信回調的接口:
public class wxNotify
{ HttpServletRequest request = (HttpServletRequest) context.getRequest();
try{
boolean flag=false;
ServletInputStream is=null;
InputStreamReader isr = null;
BufferedReader br=null;
try {
is = request.getInputStream();
isr = new InputStreamReader(is);
br =new BufferedReader(isr);
StringBuilder stb = new StringBuilder();
String s = "";
//String return_msg=(String) context.getValue("return_msg");
String return_code = "";
while ((s = br.readLine()) != null) {
stb.append(s);
}
Document document = DocumentHelper.parseText(stb.toString());
List<Element> returnCodeList = document.selectNodes("//return_code");
for (Element element : returnCodeList) {
return_code = element.getText();
}
if(return_code.equals("SUCCESS"))
{
List<Element> prepayIdList=document.selectNodes("//out_trade_no");
String out_trade_no = "";//訂單ID
for (Element element : prepayIdList) {
out_trade_no = element.getText();
}
if(StringUtils.isNotBlank(out_trade_no)){
flag=true;
}
}
}catch (Exception e) {
e.printStackTrace();
}finally{
try {
if (is != null) {
is.close();
}
if (isr != null) {
isr.close();
}
if (br != null) {
br.close();
}
} catch (IOException e) {
flag = true;
}
}
if (flag) {
context.setValue("return_code", "SUCCESS");
context.setValue("return_msg", "OK");
System.out.println("通知微信支付結果成功!!!!!!!");
// return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
} else {
context.setValue("return_code", "FAIL");
context.setValue("return_msg", "return_code_err");
// return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[return_code_err]]></return_msg></xml>";
System.out.println("通知微信支付結果失敗!!!!!!!!!");
}
} catch (NumberFormatException e) {
e.printStackTrace();
return "-1";
}
return "0";
}
}
注意:公用的類 WXPayUtil .java
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.*;
import java.security.MessageDigest;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WXPayUtil {
/**
* XML格式字符串轉換爲Map
*
* @param strXML XML字符串
* @return XML數據轉換後的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
throw ex;
}
}
/**
* 將Map轉換爲XML格式的字符串
*
* @param data Map類型數據
* @return XML格式的字符串
* @throws Exception
*/
public static String mapToXml(Map<String, String> data) throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document document = documentBuilder.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key: data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
try {
writer.close();
}
catch (Exception ex) {
}
return output;
}
/**
* 日誌
* @return
*/
public static Logger getLogger() {
Logger logger = LoggerFactory.getLogger("wxpay java sdk");
return logger;
}
/**
* md5加密
* @param str
* @return
*/
public static String md5(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0) {
i += 256;
}
if (i < 16) {
buf.append("0");
}
buf.append(Integer.toHexString(i));
}
str = buf.toString();
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
/**
* 簽名算法
* @param str
* @return
*/
public static String creatSign(SortedMap<Object, Object> parameters,String Key){
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator<?> it = es.iterator();
while (it.hasNext()) {
@SuppressWarnings("rawtypes")
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k)
&& !"key".equals(k)) {
sb.append(k + "=" + v + "&");}
}
sb.append("key="+Key);
String sign =md5(sb.toString())
.toUpperCase();
return sign;
}
//自動生成out_trade_no
public static String getCurrTime(){
Date now=new Date();
SimpleDateFormat outFormat=new SimpleDateFormat("yyyyMMddHHmmss");
String s=outFormat.format(now);
return s;
}
//產生任一位數隨機數
public static String getRandomString2(int length){
//產生隨機數
Random random=new Random();
StringBuffer sb=new StringBuffer();
//循環length次
for(int i=0; i<length; i++){
//產生0-2個隨機數,既與a-z,A-Z,0-9三種可能
int number=random.nextInt(3);
long result=0;
switch(number){
//若是number產生的是數字0;
case 0:
//產生A-Z的ASCII碼
result=Math.round(Math.random()*25+65);
//將ASCII碼轉換成字符
sb.append(String.valueOf((char)result));
break;
case 1:
//產生a-z的ASCII碼
result=Math.round(Math.random()*25+97);
sb.append(String.valueOf((char)result));
break;
case 2:
//產生0-9的數字
sb.append(String.valueOf
(new Random().nextInt(10)));
break;
}
}
return sb.toString();
}
}