微信APP支付——支付流程说明及示例

微信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】

个人实际应用后的理解及实现

  1. 生成订单(与支付宝不太一样,订单的生成是在微信支付系统中生成,而不是本地系统)。
  2. app调用接口。
  3. 异步接收返回通知,并根据结果处理自己的业务逻辑。

生成订单

  1. 组装统一下单接口参数(一个XML字符串)。
  2. 调用支付接口,发送接口参数,创建预付单。
  3. 如果成功,则会返回一个有效的prepay_id,这是app调用微信支付的一个关键参数。
  4. 将结果返回给APP端,APP端发起调用
  5. 一些细节要注意,微信的单位是“分”,支付宝的单位是“元”,所以共用订单的话要统一单位。
  6. 如果出现“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

异步接收返回通知,并根据结果处理自己的业务逻辑。

  1. 微信异步通知的返回地址是在生成订单时发送的参数里包含的(notify_url)。
  2. 接收到异步消息。返回结果是一个xml字符串 。
  3. 将返回的xml解析到map集合中。
  4. 验证返回的数据(订单是否存在,订单状态是否已经为成功,金额是否正常等)
  5. 验证通过,处理自己的业务逻辑(修改订单状态等)。
  6. 由于是跟金钱挂钩的,日志要写得完善,我本地即写库也写日志,所以代码比较乱,不用的删除即可。

注意问题

  1. 各个阶段生成的前面是不一样的,参见TenpayUtil类。
  2. 下单和返回都需要是一个参数有序的(ASCII正序)xml。
  3. 预留位置,后续更新

代码

处理类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;  
        }  


}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页