刚做完app微信支付,我这块主要负责的是后端,现在我将我怎么开发的以及踩过的坑给大家写出来
微信支付主要有3步,
第一步:是生成一个预付订单
第二步:第二次签名的信息传到app端
第三步:最后接受到微信的回调信息,并处理业务逻辑
微信api开发文档https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
1.第一步生成预付订单
package com.odfly.module.payManager.weixin.app.utils;
import com.odfly.module.component.entity.PayConfig;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
public class WxUtil {
/**
* 得到微信预付单的返回ID
*
* @param orderId 商户自己的订单号
* @param totalFee 总金额 (分)
* @param notifyUrl 回调地址
* @return
*/
public static Map<String, String> getPreyId(String orderId, String totalFee, String body, PayConfig payConfig,String notifyUrl) {
Map<String, String> reqMap = new HashMap<String, String>();
reqMap.put("appid", payConfig.getAppId());//appid
reqMap.put("mch_id", payConfig.getMchId());//商户id
reqMap.put("nonce_str", getRandomString());//随机字符串
reqMap.put("notify_url", payConfig.getDomain() + "/v2/weixin/weixinPay/"+notifyUrl); //通知地址notifyUrl
reqMap.put("body", body);//商品描述
reqMap.put("out_trade_no", orderId); //商户系统内部的订单号,
reqMap.put("total_fee", YuanTofen(totalFee)); //订单总金额,单位为分
reqMap.put("spbill_create_ip", getHostIp()); //用户端实际ip
reqMap.put("trade_type", "APP"); //交易类型
String sign = getSign(reqMap, payConfig.getAppKey());
reqMap.put("sign", sign);
String reqStr = creatXml(reqMap);
//https://api.mch.weixin.qq.com/pay/unifiedorder 统一下单api
String retStr = HttpClientUtil.postHtpps(ConstantUtil.PAY_ORDER_URL, reqStr);//ConstantUtil.PAY_ORDER_URL统一下单地址
return getInfoByXml(retStr,payConfig.getAppKey());
}
/**
* 得到本地机器的IP
*
* @return
*/
private static String getHostIp() {
String ip = "";
try {
ip = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return ip;
}
/**
* 得到加密值
*
* @param map
* @param appKey
* @return
*/
public static String getSign(Map<String, String> map, String appKey) {
String[] keys = map.keySet().toArray(new String[0]);
Arrays.sort(keys);
StringBuffer reqStr = new StringBuffer();
for (String key : keys) {
String v = map.get(key);
if (v != null && !v.equals("")) {
reqStr.append(key).append("=").append(v).append("&");
}
}
//请注意此appKey是
reqStr.append("key").append("=").append(appKey);
return WeiMd5.encode(reqStr.toString()).toUpperCase();
}
/**
* 得到随机字符串
*
* @return
*/
public static String getRandomString() {
int length = 32;
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; ++i) {
int number = random.nextInt(62);//[0,62)
sb.append(str.charAt(number));
}
return sb.toString();
}
/**
*将map文件转换成xml
*
* @param reqMap
* @return
*/
public static String creatXml(Map<String, String> reqMap) {
Set<String> set = reqMap.keySet();
StringBuffer b = new StringBuffer();
// b.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
b.append("<xml>");
for (String key : set) {
b.append("<" + key + ">").append(reqMap.get(key)).append("</" + key + ">");
}
b.append("</xml>");
return b.toString();
}
/**
* 解析从微信返回的数据,并处理部分数据仅二次签名,并将签名数据返回给app 端
* @param xmlStr
* @param appKey 商户秘钥
* @return
*/
public static Map<String, String> getInfoByXml(String xmlStr, String appKey) {
try {
Map<String, String> m = new HashMap<String, String>();
Document d = DocumentHelper.parseText(xmlStr);
Element root = d.getRootElement();
for (Iterator<?> i = root.elementIterator(); i.hasNext(); ) {
Element element = (Element) i.next();
String name = element.getName();
if (!element.isTextOnly()) {
//不是字符串 跳过。确定了微信放回的xml只有根目录
continue;
} else {
m.put(name, element.getTextTrim());
}
}
//对返回结果做校验.去除sign 字段再去加密
String retSign = m.get("sign");
m.remove("sign");
m.remove("trade_type");
m.remove("return_msg");
m.remove("result_code");
m.remove("return_code");
m.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
m.put("package", "Sign=WXPay");
//微信生成预支付订单后二次签名,注:只有以下6个字段参与签名
Map<String, String> reqMap = new HashMap<String, String>();
reqMap.put("appid",m.get("appid"));
reqMap.put("noncestr",m.get("nonce_str"));
reqMap.put("package",m.get("package"));
reqMap.put("partnerid",m.get("mch_id"));
reqMap.put("prepayid",m.get("prepay_id"));
reqMap.put("timestamp",m.get("timestamp"));
String rightSing = getSign(reqMap, appKey);
m.put("sign", rightSing);
return m;
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
2,接收微信的回调信息
private static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(WxPayService.class);
/**
*
* 微信回调
* 签约回调
* @param request
* @param response
*/
@RequestMapping(value = "/WXApp_Contract_pay_callback", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public void wxAppContractPayCallback(HttpServletRequest request, HttpServletResponse response) {
try {
DataInputStream in;
String wxNotifyXml = (String) request.getAttribute("requestParams");
Map xmlMap = XMLUtil.doXMLParse(wxNotifyXml);
// 解析各种数据
String appid = (String) xmlMap.get("appid");//应用ID
String attach = (String) xmlMap.get("attach");//商家数据包
String bank_type = (String) xmlMap.get("bank_type");//付款银行
String cash_fee = (String) xmlMap.get("cash_fee");//现金支付金额
String fee_type = (String) xmlMap.get("fee_type");//货币种类
String is_subscribe = (String) xmlMap.get("is_subscribe");//是否关注公众账号
String mch_id = (String) xmlMap.get("mch_id");//商户号
String nonce_str = (String) xmlMap.get("nonce_str");//随机字符串
String openid = (String) xmlMap.get("openid");//用户标识
String out_trade_no = (String) xmlMap.get("out_trade_no");// 获取商户订单号
String result_code = (String) xmlMap.get("result_code");// 业务结果
String return_code = (String) xmlMap.get("return_code");// SUCCESS/FAIL
String sign = (String) xmlMap.get("sign");// 获取签名
String time_end = (String) xmlMap.get("time_end");//支付完成时间
String total_fee = (String) xmlMap.get("total_fee");// 获取订单金额
String trade_type = (String) xmlMap.get("trade_type");//交易类型
String transaction_id = (String) xmlMap.get("transaction_id");//微信支付订单号
if ("SUCCESS".equals(result_code) && "SUCCESS".equals(return_code)){
//获取支付订单
PayOrder payOrder = payOrderService.get(new PayOrder().setTradeNo(out_trade_no));
if (null != payOrder) {
aliPayService.payOrderContractStatusUpate(out_trade_no,transaction_id);
}
}
} catch (Exception e) {
LOGGER.error(StringUtil.getStackMsg(e));
LOGGER.error("微信支付回调失败!" );
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("微信支付回调结束...");
}
}
3,自己遇到的坑
- 将支付信息拼凑成到map后,进行签名,这个过程中一定要用商户平台的秘钥,路径 key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
- 当你秘钥还是这样设置,且第一步获取预支付订单id签名还是错误的,并且检查签名信息正确(使用微信签名信息)的时候,果断更换秘钥,重置一个微信秘钥,这个时候签名会正确,这个微信的坑,已经遇到的第二次了
- 生成签名信息请在这里生成,然后再发送,看看返回的信息是否正确https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=20_1
- 收到异步消息。返回结果是一个xml字符串,对此xml字符串进行处理,将没有必要的字段移除掉
注:将sign去掉,然后重新组装map,进行二次签名m.remove("sign"); m.remove("trade_type"); m.remove("return_msg"); m.remove("result_code"); m.remove("return_code");
//微信生成预支付订单后二次签名,注:只有以下6个字段参与签名 Map<String, String> reqMap = new HashMap<String, String>(); reqMap.put("appid",m.get("appid")); reqMap.put("noncestr",m.get("nonce_str")); reqMap.put("package",m.get("package")); reqMap.put("partnerid",m.get("mch_id")); reqMap.put("prepayid",m.get("prepay_id")); reqMap.put("timestamp",m.get("timestamp")); String rightSing = getSign(reqMap, appKey);
以上就是个人开发的总结,有问题请留言,多谢交流