以下是本人寫的微信支付創(chuàng)單APP、H5的demo,沒有什么封裝,簡易示例。
代碼部分沒有那么規(guī)范,只是練習(xí)使用的,例如:XMLUtil、MD5Encryption這樣的命名不符合阿里P3C規(guī)約。望讀者閱讀后自行整改。
1.首先下載相關(guān)的包
log4j:請自行下載
jdom:https://mvnrepository.com/artifact/org.jdom/jdom2
如有其他缺少的包,請自行下載,下面主要看看代碼部分。
2.WeChatPayUtil
package com.alipay.api.test.my;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.erlan.common.base.http.HttpRequest;
import com.erlan.common.util.MD5Encryption;
import com.erlan.common.util.XMLUtil;
/**
* @Description: 微信支付工具類
* @author admin
* @date 2017年12月30日 下午1:55:21
* @version V1.0
*/
public class WeChatPayUtil {
private static Logger log = Logger.getLogger(WeChatPayUtil.class);
public static void main(String[] args) {
// APP支付測試
Map<String, Object> appMap = unifiedorderAPP("1000010", "1", null, "這里填寫統(tǒng)一下單回調(diào)接口(關(guān)于回調(diào)接口,下面有示例)");
// H5支付測試(創(chuàng)單成功后,返回的url必須在H5申請的支付域名之下訪問,這點(diǎn)要特別注意。而且我測試的是必須手機(jī)訪問才成功,手機(jī)!手機(jī)!手機(jī)!三遍了,老鐵們!),關(guān)于H5支付創(chuàng)單失敗的問題,我其他文章有提到
Map<String, Object> mwebMap = unifiedorderMWEB("1000012", "1", null, "這里填寫統(tǒng)一下單回調(diào)接口(關(guān)于回調(diào)接口,下面有示例)");
}
/**
* 統(tǒng)一下單(APP)
*
* @param outTradeNo 系統(tǒng)訂單號
* @param totalFee 金額(分)
* @param attach 附加數(shù)據(jù)
* @param notifyUrl 回調(diào)地址
* @author admin
* @date 2017年12月30日 下午2:18:29
* @version V1.0
*/
@SuppressWarnings("unchecked")
public static Map<String, Object> unifiedorderAPP(String outTradeNo, String totalFee, String attach, String notifyUrl) {
Map<String, Object> resultMap = new HashMap<String, Object>(8);
resultMap.put("return_code", "FAIL");
// ======================================
// 以下有注釋的代碼,需要更改對應(yīng)的值-Begin
// ======================================
// 連接商戶key
String key = "W8FFB955CA32319C3AAAE96286CC6666";
// 統(tǒng)一下單接口
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// 應(yīng)用ID(微信開放平臺審核通過的應(yīng)用APPID)
String appid = "******************";
// 商戶號(微信支付分配的商戶號)
String mch_id = "2506666250";
// 隨便的一個MD5加密字符串就行(工具類有提供,見下面)
String nonce_str = "49ba59abbe56e057";
String sign = "";
// 支付的商品信息(測試隨便輸入,支付后在微信里面看下付費(fèi)賬單信息就明白了)
String body = "**-微信支付";
String detail = "**-微信支付";
String out_trade_no = outTradeNo;
String total_fee = totalFee;
// 終端IP(改成你的服務(wù)器IP就行)
String spbill_create_ip = "10.10.10.110";
String notify_url = notifyUrl;
String trade_type = "APP";
// 這個值可以不給,如果不給的話,下面不要拼接,我下面有拼接此參數(shù),所以給它一個值,詳情官方文檔說明
attach = StringUtils.isBlank(attach) ? "no" : attach;
// ======================================
// 以上有注釋的代碼,需要更改對應(yīng)的值-End
// ======================================
// 對參數(shù)按照key=value的格式,并按照參數(shù)名ASCII字典序排序生成字符串
String signStr = "appid=" + appid + "&attach=" + attach + "&body=" + body + "&detail=" + detail + "&mch_id=" + mch_id + "&nonce_str="
+ nonce_str + "¬ify_url=" + notify_url + "&out_trade_no=" + out_trade_no + "&spbill_create_ip=" + spbill_create_ip + "&total_fee="
+ total_fee + "&trade_type=" + trade_type;
// 連接商戶key
signStr += ("&key=" + key);
System.out.println("signStr:" + signStr);
// 生成sign并轉(zhuǎn)成大寫
sign = MD5Encryption.toMD5(signStr).toUpperCase();
System.out.println(sign);
// 最終的提交xml:
String params = "<xml><appid><![CDATA[" + appid + "]]></appid><attach><![CDATA[" + attach + "]]></attach><body><![CDATA[" + body
+ "]]></body><detail><![CDATA[" + detail + "]]></detail><mch_id><![CDATA[" + mch_id + "]]></mch_id><nonce_str><![CDATA[" + nonce_str
+ "]]></nonce_str><notify_url><![CDATA[" + notify_url + "]]></notify_url><out_trade_no><![CDATA[" + out_trade_no
+ "]]></out_trade_no><spbill_create_ip><![CDATA[" + spbill_create_ip + "]]></spbill_create_ip><total_fee><![CDATA[" + total_fee
+ "]]></total_fee><trade_type><![CDATA[" + trade_type + "]]></trade_type><sign>" + sign + "</sign></xml>";
try {
System.out.println(params);
String result = HttpRequest.sendPost(url, params);
System.out.println(result);
Map<String, String> map = null;
map = XMLUtil.doXMLParse(result);
// System.out.println("return_code:" + map.get("return_code"));
if (map.get("return_code").equals("SUCCESS")) {
// 時間戳(秒)
Date date = new Date();
long time = date.getTime();
time = (time / 1000);
String timestamp = time + "";
String resultAppid = map.get("appid").toString();
String resultPartnerid = map.get("mch_id").toString();
String resultPrepayid = map.get("prepay_id").toString();
String resultPackage = "Sign=WXPay";
String resultNoncestr = map.get("nonce_str").toString();
// 對參數(shù)按照key=value的格式,并按照參數(shù)名ASCII字典序排序生成字符串
String resultSignStr = "appid=" + resultAppid + "&noncestr=" + resultNoncestr + "&package=" + resultPackage + "&partnerid="
+ resultPartnerid + "&prepayid=" + resultPrepayid + "×tamp=" + timestamp;
// 連接商戶key
resultSignStr += ("&key=" + key);
System.out.println("resultSignStr:" + resultSignStr);
// 生成sign并轉(zhuǎn)成大寫
String resultSign = MD5Encryption.toMD5(resultSignStr).toUpperCase();
System.out.println("resultSign:" + resultSign);
// APP支付所需參數(shù)(需要把這些信息返給APP開發(fā)者)
resultMap.put("return_code", "SUCCESS");
resultMap.put("appid", resultAppid);
resultMap.put("partnerid", resultPartnerid);
resultMap.put("prepayid", resultPrepayid);
resultMap.put("package", resultPackage);
resultMap.put("noncestr", resultNoncestr);
resultMap.put("timestamp", timestamp);
resultMap.put("sign", resultSign);
System.out.println(resultMap.toString());
}
}
catch (Exception e) {
// TODO Auto-generated catch block
log.error("APP統(tǒng)一下單異常");
e.printStackTrace();
}
return resultMap;
}
/**
* 統(tǒng)一下單(H5)
*
* @param outTradeNo 系統(tǒng)訂單號
* @param totalFee 金額(分)
* @param attach 附加數(shù)據(jù)
* @param notifyUrl 回調(diào)地址
* @author admin
* @date 2017年12月30日 下午2:18:29
* @version V1.0
*/
@SuppressWarnings("unchecked")
public static Map<String, Object> unifiedorderMWEB(String outTradeNo, String totalFee, String attach, String notifyUrl) {
Map<String, Object> resultMap = new HashMap<String, Object>(8);
resultMap.put("return_code", "FAIL");
// ======================================
// 以下有注釋的代碼,需要更改對應(yīng)的值-Begin
// ======================================
// 連接商戶key
String key = "W8FFB955CA32319C3AAAE96286CC6666";
// 統(tǒng)一下單接口
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// 應(yīng)用ID(微信開放平臺審核通過的應(yīng)用APPID)
String appid = "******************";
// 商戶號(微信支付分配的商戶號)
String mch_id = "2506666250";
// 隨便的一個MD5加密字符串就行(工具類有提供,見下面)
String nonce_str = "49ba59abbe56e057";
String sign = "";
// 支付的商品信息(測試隨便輸入,支付后在微信里面看下付費(fèi)賬單信息就明白了)
String body = "**-微信支付";
String detail = "**-微信支付";
String out_trade_no = outTradeNo;
String total_fee = totalFee;
// 終端IP(改成你的服務(wù)器IP就行)
String spbill_create_ip = "10.10.10.110";
String notify_url = notifyUrl;
String trade_type = "APP";
// 商戶平臺申請開通H5支付,對應(yīng)的信息,詳見官方文檔(bundle_id的值在微信開放平臺-管理中心-對應(yīng)的產(chǎn)品里面查看)
String scene_info = "{\"h5_info\": {\"type\":\"IOS\",\"app_name\": \"你的產(chǎn)品名稱\",\"bundle_id\": \"cn.**.**\"}}";
attach = StringUtils.isBlank(attach) ? "no" : attach;
// ======================================
// 以上有注釋的代碼,需要更改對應(yīng)的值-End
// ======================================
// 對參數(shù)按照key=value的格式,并按照參數(shù)名ASCII字典序排序生成字符串
String signStr = "appid=" + appid + "&attach=" + attach + "&body=" + body + "&detail=" + detail + "&mch_id=" + mch_id + "&nonce_str="
+ nonce_str + "¬ify_url=" + notify_url + "&out_trade_no=" + out_trade_no + "&scene_info=" + scene_info + "&spbill_create_ip="
+ spbill_create_ip + "&total_fee=" + total_fee + "&trade_type=" + trade_type;
// 連接商戶key
signStr += ("&key=" + key);
System.out.println("signStr:" + signStr);
// 生成sign并轉(zhuǎn)成大寫
sign = MD5Encryption.toMD5(signStr).toUpperCase();
System.out.println(sign);
// 最終的提交xml:
String params = "<xml><appid><![CDATA[" + appid + "]]></appid><attach><![CDATA[" + attach + "]]></attach><body><![CDATA[" + body
+ "]]></body><detail><![CDATA[" + detail + "]]></detail><mch_id><![CDATA[" + mch_id + "]]></mch_id><nonce_str><![CDATA[" + nonce_str
+ "]]></nonce_str><notify_url><![CDATA[" + notify_url + "]]></notify_url><out_trade_no><![CDATA[" + out_trade_no
+ "]]></out_trade_no><scene_info><![CDATA[" + scene_info + "]]></scene_info><spbill_create_ip><![CDATA[" + spbill_create_ip
+ "]]></spbill_create_ip><total_fee><![CDATA[" + total_fee + "]]></total_fee><trade_type><![CDATA[" + trade_type
+ "]]></trade_type><sign>" + sign + "</sign></xml>";
try {
System.out.println(params);
String result = HttpRequest.sendPost(url, params);
System.out.println(result);
Map<String, String> map = null;
map = XMLUtil.doXMLParse(result);
System.out.println("return_code:" + map.get("return_code"));
if (map.get("return_code").equals("SUCCESS")) {
String mwebUrl = map.get("mweb_url").toString();
// H5支付所需要的URL(必須在H5申請的支付域名之下訪問,而且我測試的是必須手機(jī)訪問才成功,手機(jī)!手機(jī)!手機(jī)!三遍了,老鐵們!)
resultMap.put("url", mwebUrl);
resultMap.put("return_code", "SUCCESS");
System.out.println(resultMap.toString());
}
}
catch (Exception e) {
// TODO Auto-generated catch block
log.error("H5統(tǒng)一下單異常");
e.printStackTrace();
}
return resultMap;
}
}
3.MD5Encryption
package com.alipay.api.test.my;
import java.security.MessageDigest;
/**
* @Description: MD5工具類
* @author admin
* @date 2018年1月12日 上午9:10:39
* @version V1.0
*/
public class MD5Encryption {
public static String toMD5(String plainText) {
try {
if ("".equals(plainText) || plainText == null) {
return "erlan";
}
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(plainText.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));
}
return buf.toString();
// System.out.println("32λ: " + buf.toString());// 32位
// System.out.println("16λ: " + buf.toString().substring(8, 24));// 16位
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
// public static void main(String[] args) {
// MD5Encryption md5 = new MD5Encryption();
// String md52 = md5.toMD5("123456ERlan");
// System.out.println(md52.toUpperCase());
// }
}
4.XMLUtil
package com.erlan.common.util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
/**
* @Description: XML解析工具類
* @author admin
* @date 2018年1月12日 上午9:10:10
* @version V1.0
*/
public class XMLUtil {
/**
* 解析xml,返回第一級元素鍵值對。如果第一級元素有子節(jié)點(diǎn),則此節(jié)點(diǎn)的值是子節(jié)點(diǎn)的xml數(shù)據(jù)。
*
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = XMLUtil.getChildrenText(children);
}
m.put(k, v);
}
// 關(guān)閉流
in.close();
return m;
}
/**
* 獲取子結(jié)點(diǎn)的xml
*
* @param children
* @return String
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if (!list.isEmpty()) {
sb.append(XMLUtil.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
/**
* 獲取xml編碼字符集
*
* @param strxml
* @return
* @throws IOException
* @throws JDOMException
*/
public static String getXMLEncoding(String strxml) throws JDOMException, IOException {
InputStream in = HttpClientUtil.String2Inputstream(strxml);
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
in.close();
return (String) doc.getProperty("encoding");
}
/**
* 支付成功,返回微信那服務(wù)器
*
* @param return_code
* @param return_msg
* @return
*/
public static String setXML(String return_code, String return_msg) {
return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
}
public static String createXML(Map<String, Object> map) {
Set<Entry<String, Object>> set = map.entrySet();
set.iterator();
return null;
}
}
4.WeChatController
這里要注意了!例如你的這個接口是針對APP支付成功后的業(yè)務(wù)處理,訪問你這個接口的地址是:http://192.168.0.110:8080//wechat/successRechargeNotify
那么上面提到的unifiedorderAPP方法里的參數(shù)notifyUrl的值就是這個接口地址,例如:
unifiedorderAPP("1000010", "1", null, "http://192.168.0.110:8080//wechat/successRechargeNotify");
package com.erlan.mobile.controller.pay;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.jdom2.JDOMException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.dubbo.config.annotation.Reference;
import com.erlan.api.service.TbOrderService;
import com.erlan.api.service.TbUserService;
import com.erlan.common.util.XMLUtil;
import com.erlan.common.util.wxpay.WeChatPayConfig;
/**
* @Description: 微信支付成功回調(diào)
* @author admin
* @date 2017年12月29日 上午9:19:25
* @version V1.0
*/
@RestController
@RequestMapping("/wechat")
public class WeChatController {
private static Logger log = Logger.getLogger(WeChatController.class);
@Reference
private TbOrderService tbOrderService;
@Reference
private TbUserService tbUserService;
/**
* 訂單支付成功回調(diào)
*
* @param param
* @return
* @author admin
* @date 2017年12月27日 下午2:21:53
* @version V1.0
*/
@SuppressWarnings({ "unchecked" })
@RequestMapping("/successPayNotify")
public String successPayNotify(HttpServletRequest request, HttpServletResponse response) {
log.info("pay......success");
String notifyResult = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
try {
System.out.println("微信支付回調(diào)");
// PrintWriter writer = response.getWriter();
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
outSteam.close();
inStream.close();
String result = new String(outSteam.toByteArray(), "utf-8");
System.out.println("微信支付通知結(jié)果:" + result);
Map<String, String> map = null;
try {
/**
* 解析微信通知返回的信息
*/
map = XMLUtil.doXMLParse(result);
}
catch (JDOMException e) {
// TODO Auto-generated catch block
log.error("微信支付回調(diào)解析異常");
e.printStackTrace();
}
System.out.println("=========:" + result);
// 若支付成功,則告知微信服務(wù)器收到通知
if (map.get("return_code").equals("SUCCESS")) {
// 這里可以寫你的系統(tǒng)業(yè)務(wù)邏輯......
}
}
catch (Exception e) {
// TODO: handle exception
log.error("微信支付回調(diào)異常");
}
System.out.println("notifyResult:" + notifyResult);
return notifyResult;
}
}