目录
1.官方微信支付流程
2.平台微信下单支付流程
3.关键代码
5.注意事项
生成微信签名代码
/**
* 生成签名
* @return 签名
*/
public static String getSign(Map map){
List list = new ArrayList<>();
Map map2 =new TreeMap<>(map);
for(Map.Entry entry:map2.entrySet()){
if(entry.getValue()!=""){
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + WeixinpayConfig.KEY;//此处待修改
result = DigestUtils.md5Hex(result).toUpperCase();
return result;
}
构造微信统一下单参数
/**
* <一句话功能简述> 构造微信统一下单参数
* <功能详细描述>
* author: zhanggj
* 创建时间: 2018年12月18日
* @param onlinePayRecord 平台在线支付记录
* @param appid 微信appId
* @param tradeType 支付类型 取值如下:JSAPI(小程序,二维码),NATIVE,APP,MWEB (H5支付)
* @param sceneInfo H5支付需要
* @param openId 小程序支付,用户openid
*/
private String struckUnifiedOrderParam(OnlinePayRecord onlinePayRecord, String appid,
String tradeType, String sceneInfo, String openId)
{
HashMap parameterMap = new HashMap<>();
parameterMap.put("appid", appid);
parameterMap.put("mch_id", WeixinpayConfig.MCH_ID);// 微信支付分配的商户号
parameterMap.put("nonce_str", RandomStringUtils.randomAlphabetic(30));// 获取随机字符串
parameterMap.put("body", onlinePayRecord.getItemNames());// 商品或支付单简要描述
parameterMap.put("out_trade_no", onlinePayRecord.getRecordId());// 户系统内部的订单号,32个字符内、可包含字母
// 订单总金额 单位分
BigDecimal payAmount = onlinePayRecord.getTotalAmount().multiply(new BigDecimal(100));
parameterMap.put("total_fee", payAmount.intValue());//订单总金额 单位分
parameterMap.put("spbill_create_ip", onlinePayRecord.getRemarks());// APP和网页支付提交用户端ip
long dateLong = System.currentTimeMillis();
parameterMap.put("time_start", DateUtils.getStrDate(new Date(dateLong),"yyyyMMddHHmmss"));//订单生成时间 (非必填)
dateLong += 10 * 60 * 1000; // 10分钟
parameterMap.put("time_expire", DateUtils.getStrDate(new Date(dateLong), "yyyyMMddHHmmss"));//订单失效时间,必须大于5分钟(非必填)
parameterMap.put("notify_url", baseNotifyUrl + WeixinpayConfig.NOTIFY_URL);// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
parameterMap.put("trade_type", tradeType); //支付类型 取值如下:JSAPI,NATIVE,APP,MWEB
// H5 支付需要
if (StringUtils.isNotBlank(sceneInfo)) {
parameterMap.put("scene_info", sceneInfo);//支付场景
}
if (StringUtils.isNotBlank(openId)) {
parameterMap.put("openid", openId);// 获取appid参数 小程序ID
}
parameterMap.put("sign", WeixinpayConfig.getSign(parameterMap)); // 获取签名
return XmlUtil.getXML(parameterMap);
}
jsapi支付
/**
* <一句话功能简述> JSAPI支付
* <功能详细描述>
* 创建时间: 2018年12月18日
* @param onlinePayRecord 平台在线支付记录
* @param openId 微信openId
* @return 前端所需微信带签名的支付信息
*/
public String wxJSAPIPay(OnlinePayRecord onlinePayRecord, String openId, int wxType)
{
// 构造统一下单参数
String xmlParam = struckUnifiedOrderParam(onlinePayRecord, WeixinpayConfig.getAppId(wxType),
WeixinpayConfig.WX_UNIFIEDORDER_TRADETYPE_JSAPI, null, openId);
// 请求微信下单接口,创建微信订单
Map rtnMap = unifiedOrder(xmlParam);
// 与app支付不同,参数使用驼峰
SortedMap parameterMap2 = new TreeMap<>();
parameterMap2.put("appId", WeixinpayConfig.getAppId(wxType));
parameterMap2.put("package", "prepay_id=" + rtnMap.get("prepay_id")); // 预付单标识
parameterMap2.put("nonceStr", RandomStringUtils.randomAlphabetic(30));
// 本来生成的时间戳是13位,但是ios必须是10位,所以截取了一下
parameterMap2.put("timeStamp", Long.parseLong(String.valueOf(System.currentTimeMillis()).toString().substring(0,10)));
parameterMap2.put("signType", "MD5");
parameterMap2.put("sign", WeixinpayConfig.getSign(parameterMap2));
return HttpUtils.showSuccess(parameterMap2);
}
接收到微信通知后将微信xml字符串转换为Map对象
/**
* 将微信xml字符串转换为Map对象
*/
public static Map getMapFromXML(String xmlString) {
// 这里用Dom的方式解析回包的最主要目的是防止API新增回包字段
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
InputStream is = getStringStream(xmlString);
Map map = new HashMap<>();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(is);
// 获取到document里面的全部结点
NodeList allNodes = document.getFirstChild().getChildNodes();
Node node;
int i=0;
while (i < allNodes.getLength()) {
node = allNodes.item(i);
if(node instanceof Element){
map.put(node.getNodeName(),node.getTextContent());
}
i++;
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return map;
}
public static InputStream getStringStream(String sInputString) {
ByteArrayInputStream tInputStringStream = null;
if (sInputString != null && !sInputString.trim().equals("")) {
tInputStringStream = new ByteArrayInputStream(sInputString.getBytes());
}
return tInputStringStream;
}
微信签名校验
/**
* <一句话功能简述> 微信签名校验
* 创建时间: 2017年9月21日
*/
public boolean verifyWeixinNotify(Map map) {
Map parameterMap = new TreeMap<>();
String sign = (String) map.get("sign");
for (String keyValue : map.keySet()) {
if(!keyValue.equals("sign")){
parameterMap.put(keyValue, map.get(keyValue));
}
}
String createSign = WeixinpayConfig.getSign(parameterMap);
if(createSign.equals(sign)){
return true;
}else{
return false;
}
}
1.下单时要记录当前所有收货信息,不要只关联收货地址ID,收货地址后续可能会变。
2.设定合理的超期时间,提前创建付款记录,不然支付超期不好处理。
3.下单时最好提前让前端传递当前订单的推荐人是谁,这是很有必要的。
4.注意提前商定好订单确认支付页面支付金额信息与实际下单时的关系,有可能前端下单时的金额和后端处理时的金额不一致;
5.下单时前端提前根据不同卖家,划分不同订单。
6.注意现金券、运费劵抵扣,按比例无法全部平等分配的问题。
7.注意下单和完成支付时,有些低优先级事项的处理,尽量通过消息或异步线程的方式去完成,不要占用太多主事项的时间(特别注意完成支付后的处理逻辑,不要由于代码而导致出现支付超期的情况)。
8.微信统一下单时所要求的ip地址,长度不要超过限制。
9.注意处理好微信code、微信openId、平台用户ID的关系,同时注意前端缓存的微信code,不要在分享的时候带给别人,导致登录到别人的平台账号上;同时微信code不是全局唯一的,只有openId是唯一的。
10.微信支持多个订单合并付款。
11.支付时需考虑分布式扩展(支付ID不要只存储在本地节点)、外网链接的问题(不要在非外网节点上进行微信预下单,导致失败)。
12.注意微信预下单名称不要超过限制(特别是合并订单支付时)。
13.注意微信下单时间戳timeStamp在不同平台上不同,IOS上必须是10位,正常是13位。
14.注意订单总金额单位为分。
15.接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。