前言
前段时间开发了一个基于微信的电商平台,里面有涉及到微信公众号支付,不断踩坑。
一、微信支付开发的基本流程
微信开发分为三个流程
- 1.通过code授权用户权限获取用户的openid
- 2.通过获取的 openid ,商户的 appid ,商户key,商户的mch_ID,统一下单 获取预下单的ID
- 3.通过预下单的ID发起支付请求。
二、通过code授权用户权限获取用户的openid
- 第一步通过code获取openID,我的队友 已经获取到了openID 这里 就跳过
三、通过openID 统一下单获取预下单的ID
- 1、建立Payinfo 实体类
public class Payinfo implements Serializable {
private static final long serialVersionUID = 5637164279924222380L;
private String appid;
private String mch_id;
private String device_info;
private String nonce_str;
private String sign;
private String body;
private String attach;
private String out_trade_no;
private int total_fee;
private String spbill_create_ip;
private String notify_url;
private String trade_type;
private String openid;
//get() set()方法
}
- 2、包装实体
Payinfo payInfo = new Payinfo();
payInfo.setAppid("xxxxxxxxxxxx"); //填公众号的APPID
payInfo.setDevice_info("WEB");
payInfo.setMch_id("xxxxxxxx");//填商户的ID
payInfo.setNonce_str(CommonUtil.create_nonce_str().replace("-", ""));//随机字符串
payInfo.setBody("xxxxxxx");//设置支付的名称
payInfo.setOut_trade_no(out_trade_no + tt);//下单的订单号 唯一
payInfo.setTotal_fee((int) total_fee);//下单的金额 单位分
payInfo.setSpbill_create_ip(CommonUtil.getIpAddr(request));//获取ip地址
payInfo.setNotify_url(notify_url);//回调的url
payInfo.setTrade_type("JSAPI");//支付类型
payInfo.setOpenid(openId);//客户的openID
随机字符串的获取 CommonUtil.java
public static String create_nonce_str() {
return UUID.randomUUID().toString().replace("-", "");
}
IP地址的获取 CommonUtil.java
public static String getIpAddr(HttpServletRequest request) {
InetAddress addr = null;
try {
addr = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
return request.getRemoteAddr();
}
byte[] ipAddr = addr.getAddress();
String ipAddrStr = "";
for (int i = 0; i < ipAddr.length; i++) {
if (i > 0) {
ipAddrStr += ".";
}
ipAddrStr += ipAddr[i] & 0xFF;
}
return ipAddrStr;
}
统一下单签名的获取(所有的签名必须转换为大写)
public String getSign(Payinfo payInfo) throws Exception {
String signTemp =
"appid=" + payInfo.getAppid()
+ "&body=" + payInfo.getBody()
+ "&device_info=" + payInfo.getDevice_info()
+ "&mch_id=" + payInfo.getMch_id()
+ "&nonce_str=" + payInfo.getNonce_str()
+ "¬ify_url=" + payInfo.getNotify_url()
+ "&openid=" + payInfo.getOpenid()
+ "&out_trade_no=" + payInfo.getOut_trade_no()
+ "&spbill_create_ip=" + payInfo.getSpbill_create_ip()
+ "&total_fee=" + payInfo.getTotal_fee()
+ "&trade_type=" + payInfo.getTrade_type()
+ "&key="xxxxxxxxxxx"; // 这个key注意为32位
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
md5.update(signTemp.getBytes("UTF-8"));
String sign = CommonUtil.byteToStr(md5.digest()).toUpperCase();
return sign;
}
- 3、将实体包装成xml
private static XStream xstream = new XStream(new XppDriver() {
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
// 增加CDATA标记
boolean cdata = true;
@SuppressWarnings("rawtypes")
public void startNode(String name, Class clazz) {
super.startNode(name, clazz);
}
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
writer.write("<![CDATA[");
writer.write(text);
writer.write("]]>");
} else {
writer.write(text);
}
}
};
}
});
public static String payInfoToXML(Payinfo pi) {
xstream.alias("xml", pi.getClass());
return xstream.toXML(pi);
}
- 4、改变xml的格式并发送到微信的服务器,获取返回的值 并且打印日志 查看信息
Map<String, String> map = CommonUtil.httpsRequestToXML(
"https://api.mch.weixin.qq.com/pay/unifiedorder",
"POST",
payInfoToXML(payInfo).replace("__", "_").replace("<![CDATA[", "").replace("]]>", ""));
System.out.println("------------------------");
System.out.println("map 是" + map);
System.out.println("return_code 是" + map.get("return_code"));
System.out.println("return_msg 是" + map.get("return_msg"));
System.out.println("prepay_id 是" + map.get("prepay_id"));
- 5、遇到的坑
- 下单时,金额报错,金额的类型是int类型,单位是分
- 下单时,签名错误,签名是根据编码从小到大排列先把支付实体包装好,最后再加上key
今天就到这里,第三步,请看下篇文章。