以下是本人写的微信支付创单APP、H5的demo,没有什么封装,简易示例。
代码部分没有那么规范,只是练习使用的,例如:XMLUtil、MD5Encryption这样的命名不符合阿里P3C规约。望读者阅读后自行整改。
1.首先下载相关的包
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, "这里填写统一下单回调接口(关于回调接口,下面有示例)");
// H5支付测试(创单成功后,返回的url必须在H5申请的支付域名之下访问,这点要特别注意。而且我测试的是必须手机访问才成功,手机!手机!手机!三遍了,老铁们!),关于H5支付创单失败的问题,我其他文章有提到
Map<String, Object> mwebMap = unifiedorderMWEB("1000012", "1", null, "这里填写统一下单回调接口(关于回调接口,下面有示例)");
}
/**
* 统一下单(APP)
*
* @param outTradeNo 系统订单号
* @param totalFee 金额(分)
* @param attach 附加数据
* @param notifyUrl 回调地址
* @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");
// ======================================
// 以下有注释的代码,需要更改对应的值-Begin
// ======================================
// 连接商户key
String key = "W8FFB955CA32319C3AAAE96286CC6666";
// 统一下单接口
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// 应用ID(微信开放平台审核通过的应用APPID)
String appid = "******************";
// 商户号(微信支付分配的商户号)
String mch_id = "2506666250";
// 随便的一个MD5加密字符串就行(工具类有提供,见下面)
String nonce_str = "49ba59abbe56e057";
String sign = "";
// 支付的商品信息(测试随便输入,支付后在微信里面看下付费账单信息就明白了)
String body = "**-微信支付";
String detail = "**-微信支付";
String out_trade_no = outTradeNo;
String total_fee = totalFee;
// 终端IP(改成你的服务器IP就行)
String spbill_create_ip = "10.10.10.110";
String notify_url = notifyUrl;
String trade_type = "APP";
// 这个值可以不给,如果不给的话,下面不要拼接,我下面有拼接此参数,所以给它一个值,详情官方文档说明
attach = StringUtils.isBlank(attach) ? "no" : attach;
// ======================================
// 以上有注释的代码,需要更改对应的值-End
// ======================================
// 对参数按照key=value的格式,并按照参数名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并转成大写
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();
// 对参数按照key=value的格式,并按照参数名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并转成大写
String resultSign = MD5Encryption.toMD5(resultSignStr).toUpperCase();
System.out.println("resultSign:" + resultSign);
// APP支付所需参数(需要把这些信息返给APP开发者)
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统一下单异常");
e.printStackTrace();
}
return resultMap;
}
/**
* 统一下单(H5)
*
* @param outTradeNo 系统订单号
* @param totalFee 金额(分)
* @param attach 附加数据
* @param notifyUrl 回调地址
* @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");
// ======================================
// 以下有注释的代码,需要更改对应的值-Begin
// ======================================
// 连接商户key
String key = "W8FFB955CA32319C3AAAE96286CC6666";
// 统一下单接口
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// 应用ID(微信开放平台审核通过的应用APPID)
String appid = "******************";
// 商户号(微信支付分配的商户号)
String mch_id = "2506666250";
// 随便的一个MD5加密字符串就行(工具类有提供,见下面)
String nonce_str = "49ba59abbe56e057";
String sign = "";
// 支付的商品信息(测试随便输入,支付后在微信里面看下付费账单信息就明白了)
String body = "**-微信支付";
String detail = "**-微信支付";
String out_trade_no = outTradeNo;
String total_fee = totalFee;
// 终端IP(改成你的服务器IP就行)
String spbill_create_ip = "10.10.10.110";
String notify_url = notifyUrl;
String trade_type = "APP";
// 商户平台申请开通H5支付,对应的信息,详见官方文档(bundle_id的值在微信开放平台-管理中心-对应的产品里面查看)
String scene_info = "{\"h5_info\": {\"type\":\"IOS\",\"app_name\": \"你的产品名称\",\"bundle_id\": \"cn.**.**\"}}";
attach = StringUtils.isBlank(attach) ? "no" : attach;
// ======================================
// 以上有注释的代码,需要更改对应的值-End
// ======================================
// 对参数按照key=value的格式,并按照参数名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并转成大写
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申请的支付域名之下访问,而且我测试的是必须手机访问才成功,手机!手机!手机!三遍了,老铁们!)
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统一下单异常");
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,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
*
* @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);
}
// 关闭流
in.close();
return m;
}
/**
* 获取子结点的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");
}
/**
* 支付成功,返回微信那服务器
*
* @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支付成功后的业务处理,访问你这个接口的地址是:http://192.168.0.110:8080//wechat/successRechargeNotify
那么上面提到的unifiedorderAPP方法里的参数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: 微信支付成功回调
* @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;
/**
* 订单支付成功回调
*
* @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("微信支付回调");
// 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("微信支付通知结果:" + result);
Map<String, String> map = null;
try {
/**
* 解析微信通知返回的信息
*/
map = XMLUtil.doXMLParse(result);
}
catch (JDOMException e) {
// TODO Auto-generated catch block
log.error("微信支付回调解析异常");
e.printStackTrace();
}
System.out.println("=========:" + result);
// 若支付成功,则告知微信服务器收到通知
if (map.get("return_code").equals("SUCCESS")) {
// 这里可以写你的系统业务逻辑......
}
}
catch (Exception e) {
// TODO: handle exception
log.error("微信支付回调异常");
}
System.out.println("notifyResult:" + notifyResult);
return notifyResult;
}
}