微信APP支付——支付流程说明及示例
官方示例图
微信支付官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_3
官方流程图:
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
步骤4:商户APP调起微信支付。api参见本章节【app端开发步骤说明】
步骤5:商户后台接收支付通知。api参见【支付结果通知API】
步骤6:商户后台查询支付结果。,api参见【查询订单API】
个人实际应用后的理解及实现
- 生成订单(与支付宝不太一样,订单的生成是在微信支付系统中生成,而不是本地系统)。
- app调用接口。
- 异步接收返回通知,并根据结果处理自己的业务逻辑。
生成订单
- 组装统一下单接口参数(一个XML字符串)。
- 调用支付接口,发送接口参数,创建预付单。
- 如果成功,则会返回一个有效的prepay_id,这是app调用微信支付的一个关键参数。
- 将结果返回给APP端,APP端发起调用
- 一些细节要注意,微信的单位是“分”,支付宝的单位是“元”,所以共用订单的话要统一单位。
- 如果出现“body不是UTF8编码”,则是在使用URL发起请求时,统一接口编码。
关键代码(UTF-8部分):
// 获取URLConnection对象对应的输出流
out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(),"UTF-8"));
// 发送请求参数
out.print(param);
app调用接口
参考官方集成文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5
异步接收返回通知,并根据结果处理自己的业务逻辑。
- 微信异步通知的返回地址是在生成订单时发送的参数里包含的(notify_url)。
- 接收到异步消息。返回结果是一个xml字符串 。
- 将返回的xml解析到map集合中。
- 验证返回的数据(订单是否存在,订单状态是否已经为成功,金额是否正常等)
- 验证通过,处理自己的业务逻辑(修改订单状态等)。
- 由于是跟金钱挂钩的,日志要写得完善,我本地即写库也写日志,所以代码比较乱,不用的删除即可。
注意问题
- 各个阶段生成的前面是不一样的,参见TenpayUtil类。
- 下单和返回都需要是一个参数有序的(ASCII正序)xml。
- 预留位置,后续更新
代码
处理类VipWXPayController
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 【会员管理】 微信支付
* @author pzr
*
*/
@SuppressWarnings({"unchecked"})
@Controller
@RequestMapping("/vipWXPay")
public class VipWXPayController {
@Resource
private VipOrderMapper vipOrderMapper;
@Resource
private VipPriceMapper vipPriceMapper;
@Resource
private UserMapper userMapper;
@Resource
private VipWxpayAsynNotifyMapper vipWxpayAsynNotifyMapper;
@Resource
private VipPayNotifyMapper vipPayNotifyMapper;
@Resource
private VipPayService vipPayService; // 支付信息处理日志
/**
* 支付日志
*/
Logger payLogger = Logger.getLogger("pay-log");
/**
* 微信支付的回调
* @return
*/
@RequestMapping(value = "/wxPay.do",method={RequestMethod.GET,RequestMethod.POST})
@ResponseBody
public void wxPay(HttpServletRequest request,HttpServletResponse response){
payLogger.info("【=================================微信回调通知开始======================================】");
payLogger.info("【微信回调通知】正在接收微信回调报文......");
String resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
try {
ServletInputStream in = request.getInputStream();
int size = request.getContentLength();
byte[] bdata = new byte[size];
in.read(bdata);
String xmlstring = new String(bdata,TenpayUtil.getCharacterEncoding(request, response));
if(!StringUtils.isEmpty(xmlstring)){
payLogger.info("【微信回调通知】微信回调报文接收成功");
Map<String,String> paramMap = XmlUtil.parseXmlToMap(xmlstring);
//打印返回结果
// for(Map.Entry<String,String> entry : paramMap.entrySet()){
// System.out.println(entry.getKey()+":"+entry.getValue());
// }
String result_code = paramMap.get("result_code").toString();
if("SUCCESS".equals(result_code.toUpperCase())){
//通过订单id和订单编号获取指定的订单信息
//存储反馈信息,不要 注释掉就可以了
VipWxpayAsynNotify vwan = saveSourceNotify(paramMap);
VipOrder vo = new VipOrder();
vo.setId(paramMap.get("attach"));
vo.setOrderNum(paramMap.get("out_trade_no"));
payLogger.info("本地订单号【"+vo.getOrderNum()+"】【微信回调通知】支付成功");
vo = vipOrderMapper.findByIdOrderNum(vo);
payLogger.info("本地订单号【"+vo.getOrderNum()+"】【微信回调通知】验证请求真实性......");
boolean flag = checkTrue(vwan,vo);//验证是否是微信返回的信息,防止“假请求”
if(flag){
//存储到后台 支付通知表里
savePayNotify(vwan, vo);
payLogger.info("用户【"+vo.getUserName()+"】本地订单号【"+vo.getOrderNum()+"】【微信回调通知】验证通过,业务处理中......");
vipPayService.vipHandle(vo);
payLogger.info("用户【"+vo.getUserName()+"】本地订单号【"+vo.getOrderNum()+"】【微信回调通知】业务处理完成");
}else{
payLogger.info("本地订单号【"+vo.getOrderNum()+"】【微信回调通知】验证未通过");
}
}else{
payLogger.info("【微信回调通知】支付失败,报错信息【"+paramMap.get("return_msg").toString()+"】");
}
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
}else{
payLogger.info("【微信回调通知】支付失败,回调报文为空");
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
} catch (Exception e) {
payLogger.info("【微信回调通知】内部异常");
e.printStackTrace();
}finally{
try {
sendToCFT(resXml,response);
} catch (IOException e) {
e.printStackTrace();
}
}
payLogger.info("【=================================微信回调通知结束======================================】");
}
/**
* 保存支付通知
* @param vwan
* @param vo
*/
private void savePayNotify(VipWxpayAsynNotify vwan, VipOrder vo) {
VipPayNotify vipPayNotify = new VipPayNotify();
vipPayNotify.setGoodsId(vo.getGoodsId());
vipPayNotify.setGoodsName(vo.getGoodsName());
vipPayNotify.setOrderCreatetime(vo.getCreatetime());
vipPayNotify.setOrderNum(vo.getOrderNum());//订单编号
vipPayNotify.setPayAmount(fenToYuan(vwan.getTotal_fee()));//总金额,单位是元,微信需要转换
vipPayNotify.setPayState(vipPayService.getPayState(vwan.getReturn_code(),VipOrder.PAYMODE_WX));//返回状态
vipPayNotify.setBusinessId(Integer.parseInt(vwan.getId()));
vipPayNotify.setPayType(VipOrder.PAYMODE_WX);
vipPayNotify.setUserId(vo.getUserId());
vipPayNotify.setUserName(vo.getUserName());
vipPayNotifyMapper.add(vipPayNotify);
}
/**
* 存储微信的原始回调信息
* @param paramMap
* @return
*/
private VipWxpayAsynNotify saveSourceNotify(Map<String, String> paramMap) {
//存储到原始表
VipWxpayAsynNotify saveObj = new VipWxpayAsynNotify();
saveObj.setAppid(paramMap.get("appid"));
saveObj.setAttach(paramMap.get("attach"));
saveObj.setBank_type(paramMap.get("bank_type"));
saveObj.setCash_fee(paramMap.get("cash_fee"));
saveObj.setCash_fee_type(paramMap.get("cash_fee_type"));
saveObj.setCoupon_count(paramMap.get("coupon_count"));
saveObj.setCoupon_fee(paramMap.get("coupon_fee"));
saveObj.setDevice_info(paramMap.get("device_info"));
saveObj.setErr_code(paramMap.get("err_code"));
saveObj.setErr_code_des(paramMap.get("err_code_des"));
saveObj.setFee_type(paramMap.get("fee_type"));
saveObj.setIs_subscribe(paramMap.get("is_subscribe"));
saveObj.setMch_id(paramMap.get("mch_id"));
saveObj.setNonce_str(paramMap.get("nonce_str"));
saveObj.setOpenid(paramMap.get("openid"));
saveObj.setOut_trade_no(paramMap.get("out_trade_no"));
saveObj.setReturn_code(paramMap.get("return_code"));
saveObj.setReturn_msg(paramMap.get("return_msg"));
saveObj.setSign(paramMap.get("sign"));
saveObj.setTime_end(paramMap.get("time_end"));
saveObj.setTotal_fee(paramMap.get("total_fee"));
saveObj.setTrade_type(paramMap.get("trade_type"));
saveObj.setTransaction_id(paramMap.get("transaction_id"));
vipWxpayAsynNotifyMapper.add(saveObj);
return saveObj;
}
/**
* 验证数据真实性
* @param vaan
* @return
*/
private boolean checkTrue(VipWxpayAsynNotify vwan,VipOrder vo) {
//一、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号
//如果不存在则说明反馈的订单号和订单id对不上
if(vo == null){
payLogger.info("【微信回调通知】【订单验证失败】未获取本地订单");
return false;
}
payLogger.info("【微信回调通知】【订单验证成功】获取本地订单");
//验证订单是否已经完成,避免重复执行会员购买业务
if(vo.getPayState().equals(VipOrder.PAYSTATE_SUCCESS) || vo.getPayState().equals(VipOrder.PAYSTATE_FINISHED)){
payLogger.info("【微信回调通知】【订单验证失败】订单已完成支付,订单状态为【"+vo.getPayState()+"】");
return false;
}
payLogger.info("【微信回调通知】【订单验证成功】订单状态正常,订单状态为【"+vo.getPayState()+"】");
//二、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额)
if(!YuanTofen(vo).equals(vwan.getTotal_fee())){
payLogger.info("【微信回调通知】【订单验证失败】订单金额异常,本地订单金额【"+YuanTofen(vo)+"】,回调订单金额【"+vwan.getTotal_fee()+"】");
return false;
}
payLogger.info("【微信回调通知】【订单验证成功】订单金额正常,订单金额为【"+vwan.getTotal_fee()+"】");
return true;
}
/**
* 生成预支付单
* 商户系统先调用该接口在微信支付服务后台生成预支付交易单
* 返回正确的预支付交易回话标识后再在APP里面调起支付
* @param vipPriceId 套餐id
* @param username 用户名称
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/createOrder.do")
@ResponseBody
public JSONObject createOrder(@RequestParam("id") String vipPriceId,
@RequestParam("username") String username,
HttpServletRequest request,
HttpServletResponse response
) {
payLogger.info("【=================================微信订单创建开始======================================】");
payLogger.info("用户【" + username + "】开始创建微信订单......");
User user = userMapper.findByName(username);//通过用户名获取用户
VipPrice vipPrice = vipPriceMapper.findById(vipPriceId);//通过套餐id获取套餐
VipOrder vo = vipPayService.addVipOrder(user, vipPrice,VipOrder.PAYMODE_WX);//创建订单
JSONObject jsonCode = wXHandle(request, response, vo);//微信的订单处理
payLogger.info("用户【" + username + "】创建微信订单成功,本地订单号【"+vo.getOrderNum()+"】");
payLogger.info("【=================================微信订单创建结束======================================】");
return jsonCode;
}
/**
* 创建订单,微信操作
* @param request
* @param response
* @param vo
* @return
*/
private JSONObject wXHandle(HttpServletRequest request,
HttpServletResponse response, VipOrder vo) {
JSONObject jsonCode = new JSONObject();
payLogger.info("用户【" + vo.getUserName() + "】【微信操作】【统一下单API】参数开始生成......");
String orderInfo = getOrderReqData(response, request, vo);
payLogger.info("用户【" + vo.getUserName() + "】【微信操作】【统一下单API】参数生成成功");
String orderJsonStr = "";
try {
payLogger.info("用户【" + vo.getUserName() + "】【微信操作】【统一下单API】开始调用......");
orderJsonStr = HttpRequest.sendPost(ConstantUtil.PAY_ORDER_URL, orderInfo);
Map<String, String> mapOrder = XmlUtil.doXMLParse(orderJsonStr);
if (mapOrder.get("return_code").toString().equals("SUCCESS")) {
payLogger.info("用户【" + vo.getUserName() + "】【微信操作】【统一下单API】调用成功,生成预付单");
//时间戳
String timestamp = OrderReqDataUtil.getTimeStamp();
jsonCode.put("appid",mapOrder.get("appid").toString());
jsonCode.put("partnerid", ConstantUtil.MCH_ID);
jsonCode.put("prepay_id", mapOrder.get("prepay_id").toString());
jsonCode.put("packageValue", ConstantUtil.PACKAGE);
jsonCode.put("timeStamp", timestamp);
jsonCode.put("nonceStr", mapOrder.get("nonce_str"));
jsonCode.put("sign",getAppSign(mapOrder,timestamp));
} else {
jsonCode.put("msg", "errorCode");
payLogger.info("用户【" + vo.getUserName() + "】【微信操作】【统一下单API】调用失败,报错信息【"+mapOrder.get("return_msg").toString()+"】");
}
} catch (Exception e) {
jsonCode.put("msg", "errorCode");
payLogger.info("用户【" + vo.getUserName() + "】【微信操作】【统一下单API】内部异常");
e.printStackTrace();
}
return jsonCode;
}
/**
* 获取APP调用微信支付的签名
* @param mapOrder
* @param timestamp
* @return
*/
private String getAppSign(Map<String, String> mapOrder, String timestamp) {
SortedMap<String, String> appParam = new TreeMap<String, String>();
appParam.put("appid", mapOrder.get("appid").toString());
appParam.put("noncestr", mapOrder.get("nonce_str"));
appParam.put("package", "Sign=WXPay");
appParam.put("partnerid", ConstantUtil.MCH_ID);
appParam.put("prepayid", mapOrder.get("prepay_id").toString());
appParam.put("timestamp", timestamp);
return TenpayUtil.genAppSignMap(appParam);
}
/**
* 统一下单
* 获取请求参数组装
*
* @param strUrl
* @return String
*/
private String getOrderReqData(HttpServletResponse response,HttpServletRequest request,VipOrder vo) {
//使用有序集合
SortedMap<String, String> dataObj = new TreeMap<String, String>();
dataObj.put("appid", ConstantUtil.APP_ID);// 获取appid参数
dataObj.put("mch_id", ConstantUtil.MCH_ID);// 微信支付分配的商户号
dataObj.put("device_info", ConstantUtil.DEVICE_INFO);//终端设备号(门店号或收银设备ID),默认请传"WEB"
dataObj.put("nonce_str", OrderReqDataUtil.getNonceStr());// 获取随机字符串
dataObj.put("body", vo.getGoodsName());// 商品或支付单简要描述
dataObj.put("detail", getDetail(vo));// 商品详情
//主要传送订单id
dataObj.put("attach", vo.getId());// 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
dataObj.put("out_trade_no", vo.getOrderNum());// 户系统内部的订单号,32个字符内、可包含字母
dataObj.put("fee_type", ConstantUtil.FEE_TYPE);//货币类型 默认人民币:CNY
dataObj.put("total_fee", YuanTofen(vo));//订单总金额 订单金额;
dataObj.put("spbill_create_ip", OrderReqDataUtil.getIpAddr(request));// APP和网页支付提交用户端ip
// dataObj.put("time_start",
// OrderReqDataUtil.getDateTime("yyyyMMddHHmmss"));//订单生成时间
// dataObj.put("time_expire",
// OrderReqDataUtil.getDateTime("yyyyMMddHHmmss"));//订单失效时间,必须大于5分钟
dataObj.put("notify_url", ConstantUtil.NOTIFY_URL);// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
dataObj.put("trade_type", ConstantUtil.TRADE_TYPE);//支付类型 取值如下:JSAPI,NATIVE,APP
dataObj.put("sign", TenpayUtil.genServerSign(dataObj));// 获取签名
//打印
// for(Map.Entry<String, String> entry : dataObj.entrySet()){
// System.out.println(entry.getKey()+":"+entry.getValue());
// }
// 生成xml参数字符串
String resObj = XmlUtil.getRequestXml(dataObj);
return resObj;
}
/**
* 获取详情json字符串
* @param vo
*/
private String getDetail(VipOrder vo) {
WXDetail wxDetail = new WXDetail();
List<WXGoodsInfo> goodsInfoList = new ArrayList<WXGoodsInfo>();
WXGoodsInfo wxGoodsInfo = new WXGoodsInfo();
wxGoodsInfo.setBody(vo.getGoodsName());
wxGoodsInfo.setGoods_id(vo.getOrderNum());
wxGoodsInfo.setGoods_name(vo.getGoodsName());
wxGoodsInfo.setPrice(YuanTofen(vo));//总价格
wxGoodsInfo.setQuantity("1");//商品数量
goodsInfoList.add(wxGoodsInfo);
wxDetail.setGoods_detail(goodsInfoList);
//转为json格式
JSONObject detailObject = JSONObject.fromObject(wxDetail);
return detailObject.toString();
}
/**
* 获取微信价格
* 原始价格为单位为元,微信单位为分,所以需要转换
* @param vo
* @return
*/
private String YuanTofen(VipOrder vo) {
BigDecimal sourcePrice = new BigDecimal(vo.getCurrentPrice());//原始价格 单位元
BigDecimal b2 = new BigDecimal("100");//将元换算为分的单位 100
String tfee=Double.toString(sourcePrice.multiply(b2).doubleValue());
// 订单金额
String total_fee = tfee.substring(0, tfee.indexOf("."));
return total_fee;
}
/**
* 将分转换为元 如:1 分= 0.01元
* @param price
* @return
*/
private String fenToYuan(String price){
BigDecimal sourcePrice = new BigDecimal(price);//原始价格 单位元
BigDecimal b2 = new BigDecimal("100");//将元换算为分的单位 100
String tfee=Double.toString(sourcePrice.divide(b2).doubleValue());
// 订单金额
return tfee;
}
/**
* 给微信返回信息,通知接收情况
* @param msg
* @param response
* @throws IOException
*/
private void sendToCFT(String msg,HttpServletResponse response) throws IOException {
String strHtml = msg;
PrintWriter out = response.getWriter();
out.println(strHtml);
out.flush();
out.close();
}
}
基础参数类 ConstantUtil
/**
* 微信支付配置
* @author pzr
*
*/
public class ConstantUtil {
/**
* 商户id
*/
public static String MCH_ID = "11111"; // 商户号 // 商户号
/**
* 应用id,appId
*/
public static String APP_ID = "11111"; // 微信开发平台应用id
/**
* APP_SECRET
*/
public static String APP_SECRET = "111111"; // 应用对应的凭证
/**
* 秘钥
*/
public static String APP_KEY = "111111"; // 商户号对应的密钥
/**
* 扩展字段
*/
public static String PACKAGE = "Sign=WXPay"; // 扩展字段 暂填写固定值Sign=WXPay
/**
* 设备号
*/
public static String DEVICE_INFO = "WEB";// 设备号 终端设备号(门店号或收银设备ID),默认请传"WEB"
/**
* 通知回调接口路径
*/
public static String NOTIFY_URL = "http://11111";
/**
* 钞票类型 人民币
*/
public static String FEE_TYPE = "CNY";
/**
* 交易类型
*/
public static String TRADE_TYPE = "APP";//交易类型
/**
* 固定的,统一下单后调用的生成预付单接口
*/
public static String PAY_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
}
工具类 OrderReqDataUtil
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
public class OrderReqDataUtil {
public static void main(String args[]){
System.out.println(getRandomStringByLength(32));
}
/**
* 生成指定长度的随机字符串
* @param length
* @return
*/
public static String getRandomStringByLength(int length) {
String base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 生成随机字符串
*
* @return
*/
public static String getNonceStr() {
Random random = new Random();
return MD5Util.MD5Encode(String.valueOf(random.nextInt(10000)), "UTF-8");
}
public static String getTimeStamp() {
return String.valueOf(System.currentTimeMillis() / 1000);
}
public static String getDateTime(String format) {
String temp_str = "";
Date dt = new Date();
// 最后的aa表示“上午”或“下午” HH表示24小时制 如果换成hh表示12小时制
SimpleDateFormat sdf = new SimpleDateFormat(format);
temp_str = sdf.format(dt);
return temp_str;
}
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
工具类 TenpayUtil
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 微信支付工具
* @author pzr
*
*/
public class TenpayUtil {
private static Object Server;
private static String QRfromGoogle;
/**
* 把对象转换成字符串
*
* @param obj
* @return String 转换成字符串,若对象为null,则返回空字符串.
*/
public static String toString(Object obj) {
if (obj == null)
return "";
return obj.toString();
}
/**
* 把对象转换为int数值.
*
* @param obj
* 包含数字的对象.
* @return int 转换后的数值,对不能转换的对象返回0。
*/
public static int toInt(Object obj) {
int a = 0;
try {
if (obj != null)
a = Integer.parseInt(obj.toString());
} catch (Exception e) {
}
return a;
}
/**
* 获取当前时间 yyyyMMddHHmmss
*
* @return String
*/
public static String getCurrTime() {
Date now = new Date();
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String s = outFormat.format(now);
return s;
}
/**
* 获取当前日期 yyyyMMdd
*
* @param date
* @return String
*/
public static String formatDate(Date date) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
String strDate = formatter.format(date);
return strDate;
}
/**
* 取出一个指定长度大小的随机正整数.
*
* @param length
* int 设定所取出随机数的长度。length小于11
* @return int 返回生成的随机数。
*/
public static int buildRandom(int length) {
int num = 1;
double random = Math.random();
if (random < 0.1) {
random = random + 0.1;
}
for (int i = 0; i < length; i++) {
num = num * 10;
}
return (int) ((random * num));
}
/**
* 获取编码字符集
*
* @param request
* @param response
* @return String
*/
public static String getCharacterEncoding(HttpServletRequest request, HttpServletResponse response) {
if (null == request || null == response) {
return "gbk";
}
String enc = request.getCharacterEncoding();
if (null == enc || "".equals(enc)) {
enc = response.getCharacterEncoding();
}
if (null == enc || "".equals(enc)) {
enc = "gbk";
}
return enc;
}
/**
* Url编码
* @param content
* @return
*/
public static String URLencode(String content) {
String URLencode;
URLencode = replace(Server.equals(content), "+", "%20");
return URLencode;
}
private static String replace(boolean equals, String string, String string2) {
return null;
}
/**
* 获取unix时间,从1970-01-01 00:00:00开始的秒数
*
* @param date
* @return long
*/
public static long getUnixTime(Date date) {
if (null == date) {
return 0;
}
return date.getTime() / 1000;
}
public static String QRfromGoogle(String chl) {
int widhtHeight = 300;
String EC_level = "L";
int margin = 0;
String QRfromGoogle;
chl = URLencode(chl);
QRfromGoogle = "http://chart.apis.google.com/chart?chs=" + widhtHeight + "x" + widhtHeight + "&cht=qr&chld="
+ EC_level + "|" + margin + "&chl=" + chl;
return QRfromGoogle;
}
/**
* 时间转换成字符串
*
* @param date
* 时间
* @param formatType
* 格式化类型
* @return String
*/
public static String date2String(Date date, String formatType) {
SimpleDateFormat sdf = new SimpleDateFormat(formatType);
return sdf.format(date);
}
/**
* 生成【调用统一下单API】阶段的签名
* 按参数名称a-z排序,
* 遇到空值的参数不参加签名。
* MD5加密并转换为大写
*/
public static String genServerSign(SortedMap<String, String> packageParams) {
StringBuffer sb = new StringBuffer();
Set es = packageParams.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 (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + ConstantUtil.APP_KEY);
System.out.println("md5 sb:" + sb);
//MD5加密并全部转换为大写
String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
return sign;
}
/**
* 生成【APP客户端调用微信支付】阶段签名
* @param params
* @return
*/
public static String genAppSignMap(SortedMap<String, String> appParams) {
StringBuilder sb = new StringBuilder();
for(Map.Entry<String, String> entry : appParams.entrySet()){
sb.append(entry.getKey());
sb.append('=');
sb.append(entry.getValue());
sb.append('&');
}
sb.append("key=");
sb.append(ConstantUtil.APP_KEY);//秘钥
String appSign = MD5Util.MD5Encode(sb.toString(),"utf-8");
return appSign;
}
/**
* 生成【APP客户端调用微信支付】阶段签名
* @param params
* @return
*/
public static String genAppSign(List<Param> params) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < params.size(); i++) {
sb.append(params.get(i).getKey());
sb.append('=');
sb.append(params.get(i).getValue());
sb.append('&');
}
sb.append("key=");
sb.append(ConstantUtil.APP_KEY);//秘钥
String appSign = MD5Util.MD5Encode(sb.toString(),"utf-8");
return appSign;
}
}
商品集合对象 WXDetail 只是为了组装成需要的json格式可以直接用jsonArray
import java.util.List;
public class WXDetail {
/**
* 微信商品详情集合对象
*/
private List<WXGoodsInfo> goods_detail;
public List<WXGoodsInfo> getGoods_detail() {
return goods_detail;
}
public void setGoods_detail(List<WXGoodsInfo> goods_detail) {
this.goods_detail = goods_detail;
}
}
商品详情对象 WXGoodsInfo
/**
* 微信商品订单详情
* @author pzr
*
*/
public class WXGoodsInfo {
/**
* 【必填】商品编号
*/
private String goods_id;
/**
* 【可选】微信支付定义的统一商品编号
*/
private String wxpay_goods_id;
/**
* 【必填】商品名称
*/
private String goods_name;
/**
* 【必填】商品数量
*/
private String quantity;
/**
* 【必填】商品单价 单位分
*/
private String price;
/**
* 【可选】商品类目id
*/
private String goods_category;
/**
* 【可选】商品描述信息 长度1000
*/
private String body;
public String getGoods_id() {
return goods_id;
}
public void setGoods_id(String goods_id) {
this.goods_id = goods_id;
}
public String getWxpay_goods_id() {
return wxpay_goods_id;
}
public void setWxpay_goods_id(String wxpay_goods_id) {
this.wxpay_goods_id = wxpay_goods_id;
}
public String getGoods_name() {
return goods_name;
}
public void setGoods_name(String goods_name) {
this.goods_name = goods_name;
}
public String getQuantity() {
return quantity;
}
public void setQuantity(String quantity) {
this.quantity = quantity;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getGoods_category() {
return goods_category;
}
public void setGoods_category(String goods_category) {
this.goods_category = goods_category;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
工具类 XmlUtil
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.xml.sax.InputSource;
public class XmlUtil {
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
*
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map<String, String> doXMLParse(String strxml) throws JDOMException, IOException {
if (null == strxml || "".equals(strxml)) {
return null;
}
Map<String, String> m = new HashMap<String, String>();
InputStream in = HttpClientUtil.String2Inputstream(strxml);
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=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(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");
}
/**
* @author wjj
* @date 2016-06-01下午2:32:05
* @Description:将请求参数转换为xml格式的string
* @param parameters 请求参数
* @return
*/
public static String getRequestXml(SortedMap<String,String> parameters){
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
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+">"+"<![CDATA["+v+"]]></"+k+">");
}else {
sb.append("<"+k+">"+v+"</"+k+">");
}
}
sb.append("</xml>");
String result =sb.toString();
// try {
// result = new String(result.getBytes("UTF-8"), "ISO-8859-1");
// } catch (UnsupportedEncodingException e) {
// e.printStackTrace();
// }
return result;
}
/**
* XML转为Map
* @param xml
* @return
*/
public static Map parseXmlToMap(String xml) {
Map retMap = new HashMap();
try {
StringReader read = new StringReader(xml);
// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
InputSource source = new InputSource(read);
// 创建一个新的SAXBuilder
SAXBuilder sb = new SAXBuilder();
// 通过输入源构造一个Document
Document doc = sb.build(source);
Element root = (Element) doc.getRootElement();// 指向根节点
List<Element> es = root.getChildren();
if (es != null && es.size() != 0) {
for (Element element : es) {
retMap.put(element.getName(), element.getValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return retMap;
}
}