前言:本人是从android 转java的,第一次加入项目就遇到了微信支付对接,微信支付的对接文档让我感觉很差,也遇到了很多的坑,所以在此记录一下。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
一:先了解微信小程序支付的流程,官方给的流程图还是可以看的
我们后台要做的就只有三点:
1.通过小程序的appid 秘钥 code 三个参数拿到小程序的openid(每个登录小程序的用户openid是唯一的)
2.然后通过openid,还有订单参数,小程序appid,商户mch_id等(开发文档有明确必传参数,下面会给代码)拿到支付id prepay_id返回给前端
(此处需要注意的就是两次签名的生成)
3.对微信后台返回的支付结果进行数据处理
二:主要代码部分
1.如何拿到小程序的openid
puclic class WeiXinUtil{
public static String getOpenid(String code){
Map params = new HashMap<>();
params.put("appid", "微信小程序开发平台注册信息");
params.put("secret", "微信小程序开发平台的秘钥");
params.put("grant_type", "authorization_code");
params.put("js_code", "微信小程序登录后传来的code参数");
Map map = JSON.parseObject(HttpClientUtil.postMap("https://api.weixin.qq.com/sns/jscode2session", params),Map.class);
System.out.println(map);
assert map != null;
if (map.get("openid") == null) {
throw new BusinessException(map.get("errmsg").toString());
}
return map.get("openid").toString();
}
}
代码段解释:params中的参数都是小程序那边给出的,grant_type:参数固定为autorization_code,然后将参数使用post请求发到微信的接口中https://api.weixin.qq.com/sns/jscode2session,从返回值中拿到openid
2.如何将商品信息签名,去微信后台验证拿到支付id prepay_id(这一步的操作就叫统一下单)
统一下单的参数给了很多,我们只传他必填参数
接口代码
@ApiOperation("微信支付,预下单")
@PostMapping("/xxxxx")
public RestResult wxpay(@RequestBody PayRequestBodyVo vo) {
try {
String unifiedorder = WeiXinUtil.getUnifiedorder(openid, outTradeNo, Double.valueOf(vo.getSumValue()));
Map map = PayUtil.doXMLParse(unifiedorder);
SortedMap
public class WeiXinUtil{
public static String getUnifiedorder(String openid, String outTradeNo, double money) throws Exception {
SortedMap
然后给出这里使用到的工具方法
public class PayUtil{
/**
* 获取当前机器的ip
*/
public static String getLocalIp() {
InetAddress ia = null;
String localip = null;
try {
ia = ia.getLocalHost();
localip = ia.getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return localip;
}
@SuppressWarnings("rawtypes")
public static String getRequestXml(SortedMap parameters) {
StringBuffer sb = new StringBuffer();
sb.append("");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + "" + k + ">");
} else {
sb.append("<" + k + ">" + v + "" + k + ">");
}
}
sb.append(" ");
return sb.toString();
}
/**
* 创建签名Sign
*/
@SuppressWarnings("rawtypes")
public static String createSign(String characterEncoding,SortedMap parameters, String key) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator> it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
if (entry.getValue() != null || !"".equals(entry.getValue())) {
String v = String.valueOf(entry.getValue());
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
}
sb.append("key=" + key);
String sign = MD5Util.MD5Encode(sb.toString()).toUpperCase();
return sign;
}
/**
* 生成随机数
*/
public static String makeUUID(int len) {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, len);
}
/**
* 生成订单号
*/
public static String generateOrderNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
return sdf.format(new Date()) + makeUUID(16);
}
/**
* 解析xml
*/
public static Map doXMLParse(String strxml) throws Exception {
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();
org.jdom2.Document doc = builder.build(in);
org.jdom2.Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
org.jdom2.Element e = (org.jdom2.Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
/**
* 获取子节点的xml
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
org.jdom2.Element e = (org.jdom2.Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("" + name + ">");
}
}
return sb.toString();
}
/**
* 转换金额到整型
*/
public static String moneyToIntegerStr(Double money) {
BigDecimal decimal = new BigDecimal(money);
int amount = decimal.multiply(new BigDecimal((100)))
.setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
return String.valueOf(amount);
}
/**
* 微信下单,map to xml
* @param params 参数
* @return String
*/
public static String mapToXml(Map params) {
StringBuilder xml = new StringBuilder();
xml.append("");
for (Map.Entry entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// 略过空值
if (StringUtils.isEmpty(value)) continue;
xml.append("<").append(key).append(">").append(key).append(">");
}
xml.append(" ");
return xml.toString();
}
public static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}
本人在此踩过的坑,1.由于是两个人合作,商户mch_id写错了,请求下单返回结果 商户有违规,限制支付功能,改正商户id即解决
2.签名错误:此处代码已经尝试过了 签名没有错误,但是本人在做这个地方的时候也遇到了些坑,请求结果为签名错误,然后去微信签名验证工具中验证:
将转化后的xml签名信息放入,然后将商户平台设置的秘钥放入商户key中,若验证通过,两个key相同 即你的签名没有问题,
若此处代码中还返回签名错误,请重新到商户账号中,重新设置秘钥,(这个时候一定是你设置的秘钥,和你代码总验证的秘钥不一致,这里的错误就多种原因了:可能设置后复制粘贴的错误,可能是商户那边操作人员给你的时候就是错的,所以在这里请重新设置一次)
三:微信返回结果接口
返回结果的接口就不上代码了,这里都是项目的个人操作,唯一需要注意的就是能否拿到微信后台返回的数据
1.返回数据是null的:本人遇到的原因是(使用花生壳做了这个接口的外网映射,由于微信开发平台要求说要https的,但测试的时候就拿不到返回值),解决方式:将映射类型变成http就可以拿到返回值了
-------------------------------------------------------------------------
---------------若有地方表达不明白,欢迎加QQ:574774295 询问,请备注验证消息