微信app支付_微信app支付开发 - CSDN
精华内容
参与话题
  • 微信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;  
            }  
    
    
    }
    
    展开全文
  • 微信支付相对支付宝支付,操作繁琐了很多,而且有些文档上的说明太过专业,导致问题多多。 首先他们官网上面只有eclipse的案列说明,没有Android studio版本的,其实也没什么太大的区别,注意一点就行了 在调用...

    之前开发过高德地图的,百度地图的,人家官网的资料,开发文档,官方论坛,应有尽有,特别详细。微信支付相对支付宝支付,操作繁琐了很多,而且有些文档上的说明太过专业,导致问题多多。

    首先他们官网上面只有eclipse的案列说明,没有Android studio版本的,其实也没什么太大的区别,注意一点就行了

    在调用微信支付之前,首先要申请微信开放平台帐号,微信商户平台帐号,还有在微信开放平台添加移动应用等等操作都要先做到位,不然是没法支付的

    1、首先你要申请微信开放平台,然后完成后,进入开放平台的管理中心,创建移动应用,应用审核成功后会直接拥有微信分享、收藏等功能,而app支付需要另外再申请

    这里写图片描述

    然后进行微信支付的申请,申请需要填写资料进行审核,而且还需要微信商户帐号,关联商户帐号,获得商户key(商户key在后面生成sign值时需要用到)

    2、服务器那边应该要操作的是,首先调用微信统一下单接口,需要用到多个参数(包括商户key)进行调用接口,成功后会获得应该返回给我们的参数,这一次返回值当中会有sign值,但是该sign值不需要用到,也不需要返回给我们app端,这里需要特别注意的一点是,后台服务需要二次签名,就是把调用统一下单接口返回的值(主要六个参数appid,partnerid,prepayid,package,noncestr,timestamp),进行二次签名得到sign,该sign就是app端需要用到的。(因为进行签名的时候需要用到商户key,商户key比较敏感,所以签名步骤需要放在服务器端进行,签名之后由app端请求返回)

    3、接下来就是介绍app端要操作的步骤,首先,你需要对你的app进行打包操作,获得keystone文件,或者,然后运行微信官网提供的app获取你的应用签名,操作如下
    这里写图片描述
    输入包名,就可以获得你的应用签名(这里需要注意的是,需要打包后的app,而不是直接运行的),然后进入微信开放平台,在你的应用那里添加。由于需要保证支付安全,需要在开放平台绑定商户应用包名和应用签名,设置好后才能正常发起支付。如下图所示

    这里写图片描述

    接下来就是app调用了,在App调用当中,首先要先注册app,就是往微信客户端添加你的应用,还要引入官网提供的jar包,相信引包的操作大家都会了,然后才可以进行app支付
    final IWXAPI msgApi = WXAPIFactory.createWXAPI(context, null);
    // 将该app注册到微信
    msgApi.registerApp(“wxd930ea5d5a258f4f”);

    app支付中,我们客户端请求服务器接口,回调需要的参数,然后根据参数进行app支付请求,,具体操作如下

    这里写图片描述
    上图是微信官网上的案例,上面有个错误的地方,大家自己改过来,还有这里的sign,是服务器那边进行二次签名得到的sign,而不是调用统一下单接口得到的sign,服务器后台的工作人员请特别注意。(本人被这个sign给坑了好几天了)

    然后就是支付回调这一块了,这一块也是超级坑爹的,我从这一块就觉得微信好霸道。
    你需要在你的应用包名相同路径下创建一个包路径wxapi,然后在下面再创建WXPayEntryActivity类,比如说你的应用包名是com.example.app,那么你就需要在com.example.app.wxapi下面创建WXPayEntryActivity类,而且微信那边特别强调,如包名或类名不一致会造成无法回调结果(微信够霸道吧)。。。而且我发现,Android studio上面开发的话,还有一个很坑爹的情况,就是as上的包名可以在build.gradle上面指定为applicationId,然后在AndroidManifest文件上的package另外命名包路径,这时候就有问题了,因为微信指定是AndroidManifest文件上的package的包名,但是你却随便路径,不与应用包名一致,结果导致你连回调结果都不可能实现,参考一下官网资料,如下图所示

    这里写图片描述
    还有配置文件记得给WXPayEntryActivity类加入android:exported=“true”,不然即使回调成功也不会出现该结果界面,也顺便加上启动模式吧android:launchMode=“singleTop”

    <activity
                android:name=".wxapi.WXPayEntryActivity"
                android:exported="true"
                android:launchMode="singleTop"
                android:theme="@android:style/Theme.NoTitleBar" />
    

    当你没其他问题时,你会回调成功,回调成功,并不一定调用支付成功
    回调中errCode值列表:
    名称描述解决方案0成功展示成功页面-1错误可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等。-2用户取消无需处理。发生场景:用户不支付了,点击取消,返回APP。

    回调中errCode值列表

    这个微信支付的流程全部搞定了,结果是一直返回-1给我,试过很多方法都不行,最后才知道sign需要经过二次签名,呵呵,官网api都没说清楚,很多地方都让人不明不白,出的问题特别多,真是有趣的微信支付。

    有关微信支付的就到此为止了,应该没有什么问题,有问题的话可以在下面留言,我会及时回复的

    展开全文
  • 微信app支付详解

    千次阅读 2018-06-28 17:40:08
    import java.text.SimpleDateFormat;import java.util.Date;import java.util.Random;import java.util.UUID;/** * 随机生成订单号 * @author 张卓 * */public class OrderNumber{ /** ...

    阿西吧,弄了一个多小时,弄错了一点全没了,,,,,,,,啥破编辑器,醉了,重新来吧。

              咳咳,不要紧张,清理一下嗓子,在开始之前,我想说说,在完成之前,我本人就是一个菜鸟,很多地方我也不会弄呢,像我这种看开发文档就头疼的人做微信支付,对着官方文档整个人都飘了,本文就是针对一些不会做又看文档头疼的人来做的,相信只要会java的菜鸟看着都能做出来,第一次写博客,希望写点对大家有帮助的东西,  在感慨一下,其实很多程序我么您第一次做的时候都想的太复杂了,做完了之后就感觉其实也没个啥,当你们看完这篇文章完成微信支付的时候,相信第一次弄的童鞋们会和我一样有这种感慨。如果这篇文章帮助到你了,请给我赞一个夸夸我。

     废话不多说 直接开始正文

            微信支付,我们做java只是后台服务端,具体的实现流程官方写的我也看不懂,下边是我自己理解的

      客户确认商品点击支付—>后台提供前端调用的加签接口---->前端拿到后台加签的参数调起支付-->微信支付--->微信回调后台提供的回调接口    

    这样微信支付就完成了,总体看起来,后台只需要做两件事

          1.给前端提供接口把订单进行加签在返还给前端

          2.给微信提供回调接口确认支付信息。

    这样说的够明白了吧,相信大家已经明白后台需要做哪些事了吧。

    在做微信支付之前,后台得获得3个重要的参数

    微信官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1点击打开链接

    1.appid  微信商户号   appkey


    这几个参数都是微信支付必要的,最重要的,所以一定要根据文档找准确了,有了这几个参数,自己封装一下,下边要用到的时候自己能调用出来就行。注意,这几个参数极为重要,所以写的时候尽量封装严实一点,不然有可能有不安全因素。


    封装完这几个参数以后,自己写一个供前端调用的接口,上代码:

    	/**
    	 * 微信app支付统一下单
    	 * @param request
    	 * @param orderNumber
    	 * @param payMoney
    	 * @param orderType
    	 * @return
    	 * @throws Exception
    	 */
    	@RequestMapping(value="/APP/weixinPay",method={RequestMethod.POST,RequestMethod.GET})
    	@ResponseBody
           /**
    *  ResultState  是我自己封装的一个返回结果的对象,有需要的后边我会附代码,很简单的,毕竟我本人水平也不好。
    *    在这里,订单加签我让前端穿了3个参数  orderNumber 订单编号 需要提供给微信的  payMoney 用户支付金额  需要提供给微信的 orderType订单类型 确定body字段的
    *  这里大家可以根据自己的业务逻辑确定参数,没必要跟我的一致,返回结果也是,大家也可应自己定义的
    */
    	public  ResultState weixinUnifiedOrder( HttpServletRequest request,
    			 @RequestParam("orderNumber")String orderNumber,
    	                 @RequestParam("payMoney")String payMoney,
    	                 @RequestParam("orderType")String orderType
    	                                       ) throws Exception{
    		
    //这里是我自己根据类型获取body的,大家可以自己定义
    		Map<String, String> result = new HashMap<>();
    		String body="";
    		String orderCode = "";
    		String total_fee = "";
    		  if(orderType.equals("1")) {
    			  body="ykcz";
    		  }
    		  if(orderType.equals("2")) {
    			  body="E卡充值";
    		  }
    		  if(orderType.equals("3")) {
    			  body="手机充值";
    		  }
    		  if(orderType.equals("4")) {
    			  body="油卡购买";
    		  }
    		  Order order = orderService.selectbyordernumber(orderNumber);
    		  if(order!=null) {
    			  if(order.getPaymentType().equals("1")) {
    				  orderCode=orderNumber;
    				 Integer money=(int)(Double.parseDouble(payMoney)*100);
    				  total_fee=String.valueOf(money);
    				    String[] initValue = new String[3];
    				    initValue[0] = orderCode;
    				    initValue[1] = total_fee;
    				    initValue[2] = body;
    //微信支付需要传参使用者的ip,这里获取了一下,可以自己网上找,懒得找的下边我会附代码
    				    String remoteAddr = GetIp.getRemoteAddr(request);
    					String ip="";
    					if (remoteAddr.length()>16) {
    						String[] split = remoteAddr.split(",");
    						for (int i = 0; i < split.length; i++) {
    							ip=split[0];
    						}
    					}else{
    						ip=remoteAddr;
    					}
    //这里就是把订单信息封装好了统一下单,微信会返回一个加完签的结果,只要判断一下结果的正确性就可以吧这个结果直接返回给前端,让前端去调起支付了
    //我把上边提到的几个参数封装到了ConfigUtils里,这里你取你自己的             appid                          微信商户号     上边数组封装的值,自己对应  ip            微信回调地址(自己写的,上边提到的第二部)  APP,看文档,你是什么支付就传//什么,body 上边定义的字段,必传,加签的时候有body的解释,也可以自己看微信官方文档   自己封装的appkey         
    					result=WeiXinPay.weixinUnifiedOrder(ConfigUtils.WXAPPappid, ConfigUtils.WXmch_id,initValue[0], initValue[1], ip, ConfigUtils.WXnotify_url, "APP", body, ConfigUtils.WXappkey);
    					if(result.get("prepay_id")!=null){
    						System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"+result);
    //						//result是微信返回的字段,详细见微信开发者文档
    //						 Map<String, String> sign = WeiXinPay.getSign3(result);
    //						 System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"+sign);
    						return new ResultState(200, "生成成功",result,ignore,ignore);
    					} 
    			  }else {
    				  return new ResultState(500, "订单已经支付过了,请勿重复支付",ignore,ignore,ignore);
    			  }
    		  }else {
    			  return new ResultState(500, "订单不存在",ignore,ignore,ignore);
    		  }
    		return new ResultState(500, "签名错误",ignore,ignore,ignore);
    	}
    这个是微信支付加签,有了这个微信支付也算完成一半了,下边是微信支付回调了,
    /**
    	 * 微信支付成功回调
    	 * @param request
    	 * @param response
    	 * @return
    	 * @throws Exception
    	 */
    //微信支付要在服务起上正式环境调试,所以这个回调接口必须是在外网上可以访问到的,不能加任何参数的
    	@ResponseBody
    	@RequestMapping(value="weiXinPayCallback")
    //这里是微信支付回调,支付完成后,微信会回调这个接口,确定是否支付成功,支付成功给微信返回一个success,让微信知道成功支付了,不然微信会隔几分钟回调一次,对于你处理成功业务逻辑很麻烦
    	public  String weiXinPayCallback(HttpServletRequest request,HttpServletResponse response) throws Exception{
                  //获取微信支付回调结果
    		String result = WeiXinPayUtil.getPostStr(request);
    		System.out.println(result);
    		Map<String, String> resuleMap = WeiXinPayUtil.xmlToMap(result);
    		if(resuleMap.get("result_code")!=null && "SUCCESS".equals(resuleMap.get("result_code"))){
    			//成功
    			//获取实际支付的钱数
    			String totalFee = resuleMap.get("total_fee");
    			//获取订单号
    			String orderNo = resuleMap.get("out_trade_no").split("_")[0];
    			//获取交易流水号
    			String tradeNo = resuleMap.get("transaction_id");
    			//修改订单状态
    			//这里是我根据订单编号修改我的订单信息,更多的字段意思看微信官方文档给出的返回结果来获取你想要的信息
    			 Order order=orderService.selectbyordernumber(orderNo);
                 order.setPaymentType("2");            //支付完成
                 order.setPayMoney(order.getCurrent());
                 order.setSerialNumber(tradeNo); 
                 order.setOrderStatus(2);
                 Integer zhifuddoilb=0;
                 int returnResult = orderService.updateByPrimaryKeySelective(order);    //更新交易表中状态
                 //判断订单表数据是否更新成功
                 if(returnResult>0){
                	//在这里处理你支付成功的业务逻辑。
                	 return  "success";
                 }else {
                	 return "fail";
                 }
    		}
    		return "fail";
    	}

    这样,微信支付就完成了






    哈哈,骗你的,如果不往下看上边代码很多地方你都不知道在哪里找了吧,看这里

    package com.union.yxj.util;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.SortedMap;
    import java.util.TreeMap;
    import java.util.UUID;
    
    import org.apache.log4j.Logger;
    
    /**
     * 微信支付
     * @author zhuozhuo
     *
     */
    public class WeiXinPay {
    	
    	/**
    	 * 日志记录
    	 */
    	private static Logger logger = Logger.getLogger(WeiXinPay.class);
    	
    	/**
    	 * 微信统一下单
    	 * @param appid 微信开发者id/微信公众号id
    	 * @param mch_id 商户号
    	 * @param key 签名密匙
    	 * @param openid	appid为微信公众号id时为用户标识,appid微信开发者id为null
    	 * @param orderCode	订单号
    	 * @param total_fee	总价 单位为分
    	 * @param attach	自定义数据
    	 * @param ip	终端ip
    	 * @param notify_url	回调地址
    	 * @param trade_type	支付类型(APP,JSAPI)
    	 * @param body	支付描述
    	 * @return
    	 * @throws Exception
    	 */
    	public static Map<String, String> weixinUnifiedOrder(String appid,String mch_id,String orderCode,String total_fee,String ip,String notify_url,String trade_type,String body,String key) throws Exception{
    		 SortedMap<String, String> condition = new TreeMap<String, String>();
    		//1应用ID
    		condition.put("appid", appid);
    		System.out.println(appid);
    		//2商户号
    		condition.put("mch_id", mch_id);
    		System.out.println(mch_id);
    		//3随机字符串
    		condition.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
    		System.out.println(UUID.randomUUID().toString().replace("-", ""));
    		//4商品描述
    		condition.put("body", body);
    		System.out.println(body);
    		//5商户订单号
    		condition.put("out_trade_no", orderCode+"_"+OrderNumber.getRandom(5));
    		System.out.println(orderCode+"_"+OrderNumber.getRandom(5));
    		//6订单总金额,单位为分
    		condition.put("total_fee", total_fee);
    		System.out.println( total_fee);
    		//7终端IP
    		condition.put("spbill_create_ip", ip);
    		System.out.println(ip);
    		//8接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
    		condition.put("notify_url", notify_url);
    		System.out.println(notify_url);
    		//9支付类型
    		condition.put("trade_type", trade_type);
    		System.out.println(trade_type);
    		//签名
    		String sign = WeiXinPayUtil.createSign("UTF-8",condition);
    		System.out.println(condition);
    		condition.put("sign", sign);
    		System.out.println("!!!!!!"+sign);
    		String xml = WeiXinPayUtil.ArrayToXml(condition);
    		System.out.println();
    		String result = WeiXinPayUtil.httpsRequest(ConfigUtils.weixinUnifiedOrderUrl, "POST", xml);
    		Map<String, String> resuleMap = WeiXinPayUtil.xmlToMap(result);
    		resuleMap.put("ip", ip);
    		logger.info(result);
    		if (result.indexOf("SUCCESS") != -1) {
    			if(resuleMap.get("return_code")!=null && "SUCCESS".equals(resuleMap.get("return_code"))){
    				//成功
    				return resuleMap;
    			}else{
    				return resuleMap;
    			}
    		}else{
    			return resuleMap;
    		}
    	}
    	
    	/**
    	 * 生成签名
    	 * @param map
    	 * @return
    	 */
    	public static Map<String,String> getSign3(Map<String,String> map){
    		try {
    			if (map!=null) {
    				HashMap<String, String> signMap = new HashMap<>();
    				String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
    				String packages = "prepay_id="+map.get("prepay_id");
    				signMap.put("appId", map.get("appid"));
    				signMap.put("timeStamp", timestamp);
    				signMap.put("nonceStr", map.get("nonce_str"));
    				signMap.put("package", packages);
    				signMap.put("signType", "MD5");
    				String sign = WeiXinPayUtil.getSign(signMap, ConfigUtils.WXappkey);
    				signMap.put("paySign", sign);
    				return signMap;
    			}
    		} catch (Exception e) {
    		}
    		return null;
    	}
    }
    

    你以为这就完了?还有呢,

    package com.union.yxj.util;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.UnsupportedEncodingException;
    import java.net.ConnectException;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.net.URLEncoder;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    import java.util.SortedMap;
    import java.util.Map.Entry;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.log4j.Logger;
    import org.dom4j.Document;
    import org.dom4j.DocumentHelper;
    import org.dom4j.Element;
    
    
    public class WeiXinPayUtil {
    	
    	/**
    	 * 日志记录
    	 */
    	private static Logger logger = Logger.getLogger(WeiXinPayUtil.class);
    	
    	
    	
    	
    	/**
    	 * 签名
    	 * @param params
    	 * @param paternerKey
    	 * @return
    	 * @throws UnsupportedEncodingException
    	 */
    	public static String getSign(Map<String, String> params, String paternerKey )
    			throws UnsupportedEncodingException {
    		String string1 = createSign(params, false);
    		String stringSignTemp = string1 + "&key=" + paternerKey;
    		System.out.println(stringSignTemp);
    		String signValue = MD5.MD5Encode(stringSignTemp).toUpperCase();
    		return  signValue;
    	}
    	
    	/**
    	 * 签名2
    	 * @param params
    	 * @return
    	 * @throws UnsupportedEncodingException
    	 */
    	public static String getSign2(Map<String, String> params)
    			throws UnsupportedEncodingException {
    		String string1 = createSign(params, false);
    		String signValue = MD5.MD5Encode(string1).toUpperCase();
    		return  signValue;
    	}
    	
    	 public static String createSign(String characterEncoding,SortedMap<String,String> parameters){
    	        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();
    	            Object v = entry.getValue();
    	            if(null != v && !"".equals(v)
    	                    && !"sign".equals(k) && !"key".equals(k)) {
    	                sb.append(k + "=" + v + "&");
    	            }
    	        }
    	        sb.append("key=" + ConfigUtils.WXappkey);//最后加密时添加商户密钥,由于key值放在最后,所以不用添加到SortMap里面去,单独处理,编码方式采用UTF-8
    	        String aa=sb.toString();
    	        String sign = MyMD5Util.MD5(aa.trim()).toUpperCase();
    	        return sign;
    	    }
    	
    	
    	/**
    	 * 排序
    	 * @param params
    	 * @param encode
    	 * @return
    	 * @throws UnsupportedEncodingException
    	 */
    	public static String createSign(Map<String, String> params, boolean encode) throws UnsupportedEncodingException {
    	    Set<String> keysSet = params.keySet();
    	    Object[] keys = keysSet.toArray();
    	    Arrays.sort(keys);
    	    StringBuffer temp = new StringBuffer();
    	    boolean first = true;
    	    for (Object key : keys) {
    	        if (first) {
    	            first = false;
    	        } else {
    	            temp.append("&");
    	    }
    	    temp.append(key).append("=");
    	    Object value = params.get(key);
    	    String valueString = "";
    	    if (null != value) {
    	        valueString = value.toString();
    	    }
    	    if (encode) {
    	        temp.append(URLEncoder.encode(valueString, "UTF-8"));
    	        } else {
    	            temp.append(valueString);
    	        }
    	    }
    	    return temp.toString();
    	}
    	
    	/**
    	 * 将map转换为xml
    	 * @param arr
    	 * @return
    	 */
    	public static String ArrayToXml(Map<String, String> arr) {
    		String xml = "<xml>";
    
    		Iterator<Entry<String, String>> iter = arr.entrySet().iterator();
    		while (iter.hasNext()) {
    			Entry<String, String> entry = iter.next();
    			String key = entry.getKey();
    			String val = entry.getValue();
    			xml += "<" + key + ">" + val + "</" + key + ">";
    		}
    
    		xml += "</xml>";
    		return xml;
    	}
    	
    	
    	/**
    	 * 解析xml为map
    	 * @param xml
    	 * @return
    	 * @throws Exception 
    	 * @throws XmlPullParserException
    	 * @throws IOException
    	 */
    	public static Map<String, String> xmlToMap(String xml) throws Exception{
    		if(ValidateUtils.isValid(xml)){
    			Map<String, String> map = new HashMap<>();
    			Document document = DocumentHelper.parseText(xml); 
    			Element root = document.getRootElement(); 
    			for (Iterator iterator = root.elementIterator(); iterator.hasNext();) { 
    	            Element e = (Element) iterator.next(); 
    	            map.put(e.getName(), e.getText()); 
    	            System.out.println(e.getName()+"="+e.getText());
    	        } 
    			return map;
    		}else{
    			return null;
    		}
    	}
    	
    	public static void main(String[] args) throws Exception {
    		Map<String, String> xmlToMap = xmlToMap("<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[openid is invalid]]></return_msg></xml>");
    		System.err.println(xmlToMap);
    	}
    	
    	
        /**
         * 请求方法
         * @param requestUrl
         * @param requestMethod
         * @param outputStr
         * @return
         */
        public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {  
              try {  
                   
                  URL url = new URL(requestUrl);  
                  HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
                  
                  conn.setDoOutput(true);  
                  conn.setDoInput(true);  
                  conn.setUseCaches(false);  
                  // 设置请求方式(GET/POST)  
                  conn.setRequestMethod(requestMethod);  
                  conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");  
                  // 当outputStr不为null时向输出流写数据  
                  if (null != outputStr) {  
                      OutputStream outputStream = conn.getOutputStream();  
                      // 注意编码格式  
                      outputStream.write(outputStr.getBytes("UTF-8"));  
                      outputStream.close();  
                  }  
                  // 从输入流读取返回内容  
                  InputStream inputStream = conn.getInputStream();  
                  InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");  
                  BufferedReader bufferedReader = new BufferedReader(inputStreamReader);  
                  String str = null;  
                  StringBuffer buffer = new StringBuffer();  
                  while ((str = bufferedReader.readLine()) != null) {  
                      buffer.append(str);  
                  }  
                  // 释放资源  
                  bufferedReader.close();  
                  inputStreamReader.close();  
                  inputStream.close();  
                  inputStream = null;  
                  conn.disconnect();  
                  return buffer.toString();  
              } catch (ConnectException ce) {  
                  logger.info("连接超时:{}"+ ce);  
              } catch (Exception e) {  
            	  logger.info("https请求异常:{}"+ e);  
              }  
              return null;  
          } 
    	    
    	/**
    	 * 微信回调参数解析
    	 * @param request
    	 * @return
    	 */
    	public static String getPostStr(HttpServletRequest request){
        	 StringBuffer sb = new StringBuffer();
        	 try {
             InputStream is = request.getInputStream();  
             InputStreamReader isr = new InputStreamReader(is, "UTF-8");  
             BufferedReader br = new BufferedReader(isr);  
             String s = "";  
            
    			while ((s = br.readLine()) != null) {  
    			     sb.append(s);  
    			 }
    		} catch (IOException e) {			
    			e.printStackTrace();
    		}  
             String xml = sb.toString(); //次即为接收到微信端发送过来的xml数据  
             logger.info(xml+"========================");
             return xml;
        }
    }
    

    结束了?不行,要坚挺!!!

    package com.union.yxj.util;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Random;
    import java.util.UUID;
    
    /**
     * 随机生成订单号
     * @author 张卓
     *
     */
    public class OrderNumber{
    	
     
      
        /**
         * 生成订单编号  时间+8位uuid
         * @return 返回订单
         */
        public static String getOrderNo1() {  
            String str = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());  
            String uuid = UUID.randomUUID().toString();
            uuid = uuid.substring(0, 8);
            return str+uuid;
        }  
        
        /**
         * 生成订单编号  时间
         * @return 返回订单
         */
        public static String getOrderNo2() {  
            //String str = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());  
        	String str = new SimpleDateFormat("HHmmssSSS").format(new Date()); 
            return str;
        }  
        
        
        
        /**
         * 产生num位随机数
         * @param num
         * @return
         */
        public static String getRandom(int num) {  
            String code = "";
            for (int i = 0; i < num; i++) {
            	int x = new Random().nextInt(9);
            	code = code + String.valueOf(x);
    		}
            return code;
        }  
        public static String getRandom2() {  
        	Random ran = new Random();  
            StringBuffer sb = new StringBuffer();  
            while(sb.length()<16) {  
                int rand = ran.nextInt()&0x7FFFFFFF;  
                sb.append(Integer.toString(rand, 36));  
            }  
            String result = sb.substring(0, 16);  
            return result;
        }
        
        
        
        public static void main(String[] args) {
    		System.out.println(getOrderNo1());
    		int x = new Random().nextInt(2);
    		System.out.println(x);
    		
    	}
    }
    

    ,你以为这就完了?再来点好的。

    package com.union.yxj.vo;
    
    import lombok.Data;
    
    @Data
    public class ResultState {
    	/**
    	 * 结果状态(200:成功 500:失败)
    	 */
    	private int  code;
    
    	/**
    	 * 结果信息
    	 */
    	private String msg;
    	
    	/**
    	 * 封装对象
    	 * @return
    	 */
    
    	private Object object;
    	private Object obj;
    	private Object ob;
    	
    
    	public ResultState(int code, String msg) {
    		super();
    		this.code = code;
    		this.msg = msg;
    	}
    
    	public ResultState(int code, String msg, Object object) {
    		super();
    		this.code = code;
    		this.msg = msg;
    		this.object = object;
    	}
    	public ResultState(int code, String msg, Object object,Object obj) {
    		super();
    		this.code = code;
    		this.msg = msg;
    		this.object = object;
    		this.obj=obj;
    	}
    	
    
    	public int getCode() {
    		return code;
    	}
    
    	public void setCode(int code) {
    		this.code = code;
    	}
    
    	public String getMsg() {
    		return msg;
    	}
    
    	public void setMsg(String msg) {
    		this.msg = msg;
    	}
    
    	public Object getObject() {
    		return object;
    	}
    
    	public void setObject(Object object) {
    		this.object = object;
    	}
    
    	public ResultState(int code, String msg, Object object, Object obj, Object ob) {
    		super();
    		this.code = code;
    		this.msg = msg;
    		this.object = object;
    		this.obj = obj;
    		this.ob = ob;
    	}
    	
    	
    	
    }
    

    其实我还能在坚挺一会的。。。。。。。

    /**
    	 * 生成签名
    	 * @param map
    	 * @return
    	 */
    	public static Map<String,String> getSign3(Map<String,String> map){
    		try {
    			if (map!=null) {
    				HashMap<String, String> signMap = new HashMap<>();
    				String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
    				String packages = "prepay_id="+map.get("prepay_id");
    				signMap.put("appId", map.get("appid"));
    				signMap.put("timeStamp", timestamp);
    				signMap.put("nonceStr", map.get("nonce_str"));
    				signMap.put("package", packages);
    				signMap.put("signType", "MD5");
    				String sign = WeiXinPayUtil.getSign(signMap, ConfigUtils.WXappkey);
    				signMap.put("paySign", sign);
    				return signMap;
    			}
    		} catch (Exception e) {
    		}
    		return null;
    	}

    这..下..大家.......满........足......了........吧.........吧...........吧..............

    哈哈,开个小玩笑,到这里,微信支付就算是完成了,如果这篇文章帮助到你了,记得点赞哦!!!

    展开全文
  • 微信支付-App支付服务端详解

    万次阅读 多人点赞 2016-11-24 17:45:18
    微信App支付服务端详解引言主要实现app支付统一下单、异步通知、调起支付接口、支付订单查询、申请退款、查询退款功能;封装了https对发起退款的证书校验、签名、xml解析等。支付流程具体支付流程参考“微信APP”...

    微信App支付服务端详解

    引言

    主要实现app支付统一下单、异步通知、调起支付接口、支付订单查询、申请退款、查询退款功能;封装了https对发起退款的证书校验、签名、xml解析等。

    支付流程

    具体支付流程参考“微信APP”文档,文档地址

    APP支付:APP端点击下单—-服务端生成订单,并调起“统一下单”,返回app支付所需参数—–APP端“调起支付接口“,发起支付—-微信服务器端调用服务端回调地址—–服务端按照“支付结果通知”,处理支付结果

    app查询:调起“查询订单”

    APP退款:发起退款请求,调用“申请退款”,发起退款,需双向证书验证

    APP退款查询:调起“查询退款”

    APP支付Api列表

    支付代码实现

    代码实现签名、证书校验、http和https封装等,项目结构如下:

    项目结构

    支付代码

    包含支付、支付查询、异步通知、退款申请、退款查询

    package org.andy.wxpay.controller;
    
    import java.math.BigDecimal;
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.andy.wxpay.model.JsonResult;
    import org.andy.wxpay.model.ResponseData;
    import org.andy.wxpay.utils.CollectionUtil;
    import org.andy.wxpay.utils.ConfigUtil;
    import org.andy.wxpay.utils.FileUtil;
    import org.andy.wxpay.utils.HttpUtils;
    import org.andy.wxpay.utils.PayUtil;
    import org.andy.wxpay.utils.SerializerFeatureUtil;
    import org.andy.wxpay.utils.StringUtil;
    import org.andy.wxpay.utils.WebUtil;
    import org.andy.wxpay.utils.XmlUtil;
    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 com.alibaba.fastjson.JSON;
    
    /**
     * 创建时间:2016年11月2日 下午4:16:32
     * 
     * @author andy
     * @version 2.2
     */
    @Controller
    @RequestMapping("/order")
    public class PayController {
    
        private static final Logger LOG = Logger.getLogger(PayController.class);
    
        private static final String ORDER_PAY = "https://api.mch.weixin.qq.com/pay/unifiedorder"; // 统一下单
    
        private static final String ORDER_PAY_QUERY = "https://api.mch.weixin.qq.com/pay/orderquery"; // 支付订单查询
    
        private static final String ORDER_REFUND = "https://api.mch.weixin.qq.com/secapi/pay/refund"; // 申请退款
    
        private static final String ORDER_REFUND_QUERY = "https://api.mch.weixin.qq.com/pay/refundquery"; // 申请退款
    
        private static final String APP_ID = ConfigUtil.getProperty("wx.appid");
    
        private static final String MCH_ID = ConfigUtil.getProperty("wx.mchid");
    
        private static final String API_SECRET = ConfigUtil.getProperty("wx.api.secret");
    
        /**
         * 支付下订单
         * 
         * @param request
         * @param response
         * @param cashnum
         *            支付金额
         * @param mercid
         *            商品id
         * @param callback
         */
        @RequestMapping(value = "/pay", method = RequestMethod.POST)
        public void orderPay(HttpServletRequest request, HttpServletResponse response,
                @RequestParam(required = false, defaultValue = "0") Double cashnum, String mercid, String callback) {
            LOG.info("[/order/pay]");
            if (!"001".equals(mercid)) {
                WebUtil.response(response, WebUtil.packJsonp(callback, JSON
                        .toJSONString(new JsonResult(-1, "商品不存在", new ResponseData()), SerializerFeatureUtil.FEATURES)));
            }
    
            Map<String, String> restmap = null;
            boolean flag = true; // 是否订单创建成功
            try {
                String total_fee = BigDecimal.valueOf(cashnum).multiply(BigDecimal.valueOf(100))
                        .setScale(0, BigDecimal.ROUND_HALF_UP).toString();
                Map<String, String> parm = new HashMap<String, String>();
                parm.put("appid", APP_ID);
                parm.put("mch_id", MCH_ID);
                parm.put("device_info", "WEB");
                parm.put("nonce_str", PayUtil.getNonceStr());
                parm.put("body", "测试付费");
                parm.put("attach", "Andy");
                parm.put("out_trade_no", PayUtil.getTradeNo());
                parm.put("total_fee", total_fee);
                parm.put("spbill_create_ip", PayUtil.getRemoteAddrIp(request));
                parm.put("notify_url", "https://www.andy.org/wxpay/order/pay/notify.shtml");
                parm.put("trade_type", "APP");
                parm.put("sign", PayUtil.getSign(parm, API_SECRET));
    
                String restxml = HttpUtils.post(ORDER_PAY, XmlUtil.xmlFormat(parm, false));
                restmap = XmlUtil.xmlParse(restxml);
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            }
    
            Map<String, String> payMap = new HashMap<String, String>();
            if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code"))) {
                payMap.put("appid", APP_ID);
                payMap.put("partnerid", MCH_ID);
                payMap.put("prepayid", restmap.get("prepay_id"));
                payMap.put("package", "Sign=WXPay");
                payMap.put("noncestr", PayUtil.getNonceStr());
                payMap.put("timestamp", PayUtil.payTimestamp());
                try {
                    payMap.put("sign", PayUtil.getSign(payMap, API_SECRET));
                } catch (Exception e) {
                    flag = false;
                }
            }
    
            if (flag) {
                WebUtil.response(response,
                        WebUtil.packJsonp(callback,
                                JSON.toJSONString(new JsonResult(1, "订单获取成功", new ResponseData(null, payMap)),
                                        SerializerFeatureUtil.FEATURES)));
            } else {
                if (CollectionUtil.isNotEmpty(restmap)) {
                    LOG.info("订单创建失败:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));
                }
                WebUtil.response(response, WebUtil.packJsonp(callback, JSON
                        .toJSONString(new JsonResult(-1, "订单获取失败", new ResponseData()), SerializerFeatureUtil.FEATURES)));
            }
        }
    
    
        /**
         * 查询支付结果
         * 
         * @param request
         * @param response
         * @param tradeid  微信交易订单号
         * @param tradeno 商品订单号
         * @param callback
         */
        @RequestMapping(value = "/pay/query", method = RequestMethod.POST)
        public void orderPayQuery(HttpServletRequest request, HttpServletResponse response, String tradeid, String tradeno,
                String callback) {
            LOG.info("[/order/pay/query]");
            if (StringUtil.isEmpty(tradeno) && StringUtil.isEmpty(tradeid)) {
                WebUtil.response(response, WebUtil.packJsonp(callback, JSON
                        .toJSONString(new JsonResult(-1, "订单号不能为空", new ResponseData()), SerializerFeatureUtil.FEATURES)));
            }
    
            Map<String, String> restmap = null;
            try {
                Map<String, String> parm = new HashMap<String, String>();
                parm.put("appid", APP_ID);
                parm.put("mch_id", MCH_ID);
                parm.put("transaction_id", tradeid);
                parm.put("out_trade_no", tradeno);
                parm.put("nonce_str", PayUtil.getNonceStr());
                parm.put("sign", PayUtil.getSign(parm, API_SECRET));
    
                String restxml = HttpUtils.post(ORDER_PAY_QUERY, XmlUtil.xmlFormat(parm, false));
                restmap = XmlUtil.xmlParse(restxml);
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            }
    
            if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code"))) {
                // 订单查询成功 处理业务逻辑
                LOG.info("订单查询:订单" + restmap.get("out_trade_no") + "支付成功");
                WebUtil.response(response, WebUtil.packJsonp(callback, JSON
                        .toJSONString(new JsonResult(1, "订单支付成功", new ResponseData()), SerializerFeatureUtil.FEATURES)));
            } else {
                if (CollectionUtil.isNotEmpty(restmap)) {
                    LOG.info("订单支付失败:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));
                }
                WebUtil.response(response, WebUtil.packJsonp(callback, JSON
                        .toJSONString(new JsonResult(-1, "订单支付失败", new ResponseData()), SerializerFeatureUtil.FEATURES)));
            }
        }
    
    
        /**
         * 订单支付微信服务器异步通知
         * 
         * @param request
         * @param response
         */
        @RequestMapping("/pay/notify")
        public void orderPayNotify(HttpServletRequest request, HttpServletResponse response) {
            LOG.info("[/order/pay/notify]");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/xml");
            try {
                ServletInputStream in = request.getInputStream();
                String resxml = FileUtil.readInputStream2String(in);
                Map<String, String> restmap = XmlUtil.xmlParse(resxml);
                LOG.info("支付结果通知:" + restmap);
                if ("SUCCESS".equals(restmap.get("result_code"))) {
                    // 订单支付成功 业务处理
                    String out_trade_no = restmap.get("out_trade_no"); // 商户订单号
                    // 通过商户订单判断是否该订单已经处理 如果处理跳过 如果未处理先校验sign签名 再进行订单业务相关的处理
    
                    String sing = restmap.get("sign"); // 返回的签名
                    restmap.remove("sign");
                    String signnow = PayUtil.getSign(restmap, API_SECRET);
                    if (signnow.equals(sing)) {
                        // 进行业务处理
                        LOG.info("订单支付通知: 支付成功,订单号" + out_trade_no);
    
                        // 处理成功后相应给响应xml   
                        Map<String, String> respMap = new HashMap<>();
                        respMap = new HashMap<String, String>();  
                        respMap.put("return_code", "SUCCESS");  //相应给微信服务器
                        respMap.put("return_msg", "OK");  
                        String resXml = XmlUtil.xmlFormat(restmap, true); 
                        response.getWriter().write(resXml); 
                    } else {
                        LOG.info("订单支付通知:签名错误");
                    }
                } else {
                    LOG.info("订单支付通知:支付失败," + restmap.get("err_code") + ":" + restmap.get("err_code_des"));
                }
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            }
        }
    
        /**
         * 订单退款 需要双向证书验证
         * 
         * @param request
         * @param response
         * @param tradeno 微信订单号
         * @param orderno  商家订单号
         * @param callback
         */
        @RequestMapping(value = "/pay/refund", method = RequestMethod.POST)
        public void orderPayRefund(HttpServletRequest request, HttpServletResponse response, String tradeno, String orderno,
                String callback) {
            LOG.info("[/pay/refund]");
            if (StringUtil.isEmpty(tradeno) && StringUtil.isEmpty(orderno)) {
                WebUtil.response(response, WebUtil.packJsonp(callback, JSON
                        .toJSONString(new JsonResult(-1, "订单号不能为空", new ResponseData()), SerializerFeatureUtil.FEATURES)));
            }
    
            Map<String, String> restmap = null;
            try {
                Map<String, String> parm = new HashMap<String, String>();
                parm.put("appid", APP_ID);
                parm.put("mch_id", MCH_ID);
                parm.put("nonce_str", PayUtil.getNonceStr());
                parm.put("transaction_id", tradeno); 
                parm.put("out_trade_no", orderno);//订单号
                parm.put("out_refund_no", PayUtil.getRefundNo()); //退款单号
                parm.put("total_fee", "10"); // 订单总金额 从业务逻辑获取
                parm.put("refund_fee", "10"); // 退款金额
                parm.put("op_user_id", MCH_ID);
                parm.put("refund_account", "REFUND_SOURCE_RECHARGE_FUNDS");//退款方式
                parm.put("sign", PayUtil.getSign(parm, API_SECRET));
    
                //String restxml = HttpUtils.posts(ORDER_REFUND, XmlUtil.xmlFormat(parm, false));
                String restxml = HttpUtils.posts(ORDER_REFUND, XmlUtil.xmlFormat(parm, false));
                restmap = XmlUtil.xmlParse(restxml);
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            } 
    
            Map<String, String> refundMap = new HashMap<>();
            if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code"))) {
                refundMap.put("transaction_id", restmap.get("transaction_id"));
                refundMap.put("out_trade_no", restmap.get("out_trade_no"));
                refundMap.put("refund_id", restmap.get("refund_id"));
                refundMap.put("out_refund_no", restmap.get("out_refund_no"));
                LOG.info("订单退款:订单" + restmap.get("out_trade_no") + "退款成功,商户退款单号" + restmap.get("out_refund_no") + ",微信退款单号"
                        + restmap.get("refund_id"));
                WebUtil.response(response,
                        WebUtil.packJsonp(callback,
                                JSON.toJSONString(new JsonResult(1, "订单获取成功", new ResponseData(null, refundMap)),
                                        SerializerFeatureUtil.FEATURES)));
            } else {
                if (CollectionUtil.isNotEmpty(restmap)) {
                    LOG.info("订单退款失败:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));
                }
                WebUtil.response(response, WebUtil.packJsonp(callback, JSON
                        .toJSONString(new JsonResult(-1, "订单退款失败", new ResponseData()), SerializerFeatureUtil.FEATURES)));
            }
        }
    
        /**
         * 订单退款查询
         * @param request
         * @param response
         * @param tradeid 微信订单号
         * @param tradeno 商户订单号
         * @param refundid 微信退款号
         * @param refundno 商家退款号
         * @param callback
         */
        @RequestMapping(value = "/pay/refund/query", method = RequestMethod.POST)
        public void orderPayRefundQuery(HttpServletRequest request, HttpServletResponse response, String refundid,
                String refundno, String tradeid, String tradeno, String callback) {
            LOG.info("[/pay/refund/query]");
            if (StringUtil.isEmpty(tradeid) && StringUtil.isEmpty(tradeno)
                    && StringUtil.isEmpty(refundno) && StringUtil.isEmpty(refundid)) {
                WebUtil.response(response, WebUtil.packJsonp(callback, JSON
                        .toJSONString(new JsonResult(-1, "退单号或订单号不能为空", new ResponseData()), SerializerFeatureUtil.FEATURES)));
            }
    
            Map<String, String> restmap = null;
            try {
                Map<String, String> parm = new HashMap<String, String>();
                parm.put("appid", APP_ID);
                parm.put("mch_id", MCH_ID);
                parm.put("transaction_id", tradeid);
                parm.put("out_trade_no", tradeno);
                parm.put("refund_id", refundid);
                parm.put("out_refund_no", refundno);
                parm.put("nonce_str", PayUtil.getNonceStr());
                parm.put("sign", PayUtil.getSign(parm, API_SECRET));
    
                String restxml = HttpUtils.post(ORDER_REFUND_QUERY, XmlUtil.xmlFormat(parm, false));
                restmap = XmlUtil.xmlParse(restxml);
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            }
    
            Map<String, String> refundMap = new HashMap<>();
            if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code")) && "SUCCESS".equals(restmap.get("result_code"))) {
                // 订单退款查询成功 处理业务逻辑
                LOG.info("退款订单查询:订单" + restmap.get("out_trade_no") + "退款成功,退款状态"+ restmap.get("refund_status_0"));
                refundMap.put("transaction_id", restmap.get("transaction_id"));
                refundMap.put("out_trade_no", restmap.get("out_trade_no"));
                refundMap.put("refund_id", restmap.get("refund_id_0"));
                refundMap.put("refund_no", restmap.get("out_refund_no_0"));
                refundMap.put("refund_status", restmap.get("refund_status_0"));
                WebUtil.response(response, WebUtil.packJsonp(callback, JSON
                        .toJSONString(new JsonResult(1, "订单退款成功", new ResponseData(null, refundMap)), SerializerFeatureUtil.FEATURES)));
            } else {
                if (CollectionUtil.isNotEmpty(restmap)) {
                    LOG.info("订单退款失败:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));
                }
                WebUtil.response(response, WebUtil.packJsonp(callback, JSON
                        .toJSONString(new JsonResult(-1, "订单退款失败", new ResponseData()), SerializerFeatureUtil.FEATURES)));
            }
        }
    
    }
    

    微信支付接口参数含义具体参考微信APP支付文档。

    微信支付工具类

    包含签名、订单号、退单号、随机串、服务器ip地址、客户端ip地址等方法。

    package org.andy.wxpay.utils;
    
    import java.io.UnsupportedEncodingException;
    import java.net.URLEncoder;
    import java.util.Arrays;
    import java.util.Date;
    import java.util.Map;
    import java.util.Set;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * 创建时间:2016年11月2日 下午7:12:44
     * 
     * @author andy
     * @version 2.2
     */
    
    public class PayUtil {
    
        /**
         * 生成订单号
         * 
         * @return
         */
        public static String getTradeNo() {
            // 自增8位数 00000001
            return "TNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";
        }
    
        /**
         * 退款单号
         * 
         * @return
         */
        public static String getRefundNo() {
            // 自增8位数 00000001
            return "RNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";
        }
    
        /**
         * 退款单号
         * 
         * @return
         */
        public static String getTransferNo() {
            // 自增8位数 00000001
            return "TNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";
        }
    
        /**
         * 返回客户端ip
         * 
         * @param request
         * @return
         */
        public static String getRemoteAddrIp(HttpServletRequest request) {
            String ip = request.getHeader("X-Forwarded-For");
            if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
                // 多次反向代理后会有多个ip值,第一个ip才是真实ip
                int index = ip.indexOf(",");
                if (index != -1) {
                    return ip.substring(0, index);
                } else {
                    return ip;
                }
            }
            ip = request.getHeader("X-Real-IP");
            if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
                return ip;
            }
            return request.getRemoteAddr();
        }
    
        /**
         * 获取服务器的ip地址
         * 
         * @param request
         * @return
         */
        public static String getLocalIp(HttpServletRequest request) {
            return request.getLocalAddr();
        }
    
        public static String getSign(Map<String, String> params, String paternerKey) throws UnsupportedEncodingException {
            return MD5Utils.getMD5(createSign(params, false) + "&key=" + paternerKey).toUpperCase();
        }
    
        /**
         * 构造签名
         * 
         * @param params
         * @param encode
         * @return
         * @throws UnsupportedEncodingException
         */
        public static String createSign(Map<String, String> params, boolean encode) throws UnsupportedEncodingException {
            Set<String> keysSet = params.keySet();
            Object[] keys = keysSet.toArray();
            Arrays.sort(keys);
            StringBuffer temp = new StringBuffer();
            boolean first = true;
            for (Object key : keys) {
                if (key == null || StringUtil.isEmpty(params.get(key))) // 参数为空不参与签名
                    continue;
                if (first) {
                    first = false;
                } else {
                    temp.append("&");
                }
                temp.append(key).append("=");
                Object value = params.get(key);
                String valueStr = "";
                if (null != value) {
                    valueStr = value.toString();
                }
                if (encode) {
                    temp.append(URLEncoder.encode(valueStr, "UTF-8"));
                } else {
                    temp.append(valueStr);
                }
            }
            return temp.toString();
        }
    
        /**
         * 创建支付随机字符串
         * @return
         */
        public static String getNonceStr(){
            return RandomUtil.randomString(RandomUtil.LETTER_NUMBER_CHAR, 32);
        }
    
        /**
         * 支付时间戳
         * @return
         */
        public static String payTimestamp() {
            return Long.toString(System.currentTimeMillis() / 1000);
        }
    }
    

    其他所需工具类参考项目源码

    支付结果

    支付结果

    APP支付测试完成

    源代码地址:微信APP支付

    展开全文
  • 微信APP支付-Java后台实现

    万次阅读 热门讨论 2020-09-23 17:13:42
    因为自己项目上的APP 原来有支付宝支付,现在想要加上微信支付,所以去研究了微信APP支付的相关技术文档。虽然微信的相关的技术文档已经非常的清楚了。但是我还是想记录一下自己研究过程。 1 注册微信开放者平台...
  • 微信APP支付服务端和Android 端详解及其demo

    万次阅读 多人点赞 2019-08-05 17:28:18
    最近在开发APP微信支付和支付宝支付,Android 端和后端都是我自己开发的,发现两家公司的文档都不是很友好,特别是微信,接触过或者开发过的人都应该有所体会。因此我特意把开发的过程梳理了,做下记录,方便以后...
  • 最近项目使用uni-app实现微信支付,把过程简单记录下,帮助那些刚刚基础uni-app,苦于文档的同学们。 整体来说实现过程和非uni-app的实现方式没有太大不同,难点就在于uni-app对于orderInfo的格式没有说明。 准备...
  • 微信app支付(后端代码)

    千次阅读 热门讨论 2018-09-25 23:14:36
    一、准备 接入步骤: 接口文档地址:
  • 手机浏览器唤起微信app支付说明

    万次阅读 多人点赞 2016-07-08 09:58:59
    微信支付官方文档并没有显示h5唤起微信app支付的文档,但是自微信6.0.2版本后已支持该功能,而且官方已经有了开发文档,只是没有显示出来。 微信h5支付文档地址: ...
  • 1-1) 查看微信支付 appid 的方法 微信支付使用的 appid, 是微信服务号的 appid, ...微信支付使用的 appsecret, 是微信服务号的 appsecret, 登录你的微信服务号后台, 在 开发-基本配置/开发者密码(App...
  • 微信APP支付申请配置过程详解

    千次阅读 2018-06-07 11:04:50
    第一步第二步:填写应用信息第二步第三步:创建应用之后等待审核第三步第四步:详情中如果微信或获得支付能力,进行申请开通第四步第五步:申请支付能力第五步第六步:登陆商户平台进行最后的配置第六步最后,进行...
  • 关于微信APP支付,提示支付验证签名失败

    万次阅读 热门讨论 2017-04-12 11:58:22
    微信APP支付,在“统一下单”里加签没有问题,调用成功,但是在 “调起支付”后却提示 支付验证签名失败。
  • 原因很简单,其实只要你认真阅读微信支付文档就不会出现这种问题了,解决如图
  • 微信支付要细心,仔细才不会走弯路 1、要是按照他上面的驼峰原则来进行签名,那你就大错特错了。 记住:这几个字段的“字段名”在参与签名的时候一定都要改成小写!!! 2、还有个坑要注意:不仔细根本...
  • 微信官方实在太坑了,不仅不给demo,还在文档中误导人!!!下面来说说要注意的两个坑 1、在第二次签名时候,官方给出的说法是如图: 要是按照他上面的驼峰原则来进行签名,那你就大错特错了。 记住:这几个...
  • uniapp app 端调微信支付接口

    万次阅读 2019-05-31 18:57:17
    uni-app 官方文档支付接口如下: uni.requestPayment({ provider: provider, // wxpay、alipay orderInfo: 'orderInfo', //微信、支付宝订单数据 success: function (res) { console.log('success:' + JSON....
  • iOS 封装的H5 APP 调用微信H5支付,进入微信支付界面后,点击取消,或者支付完成返回不能返回到App,而是打开了手机系统的safari浏览器。 用的h5+ 请问有解决方案吗
  • 在iOS的webview中,h5自己调用微信支付,能够成功起调,并且能完成支付,但是点击完成时,跳转到safari浏览器中,并不能跳回app
  • 在iOS APP发起H5微信支付

    万次阅读 2018-01-11 14:43:46
    场景介绍 H5支付是指商户在微信客户端外的移动端网页展示...提醒:H5支付不建议在APP端使用,如需要在APP中使用微信支付,请接APP支付。 这个场景介绍来自微信H5支付的开发文档,说建议不要在APP端使用H5支付,但是
  • 1、 先开通APP支付(SDK方式),通过微信开放平台开通(300元)现在审核很严格。申请完成后通过邮件下达商户账号,商户平台绑定公司的对公银行账户。 2、现在开通公众号(服务号)300元,申请支付审核,下达了新的...
1 2 3 4 5 ... 20
收藏数 48,111
精华内容 19,244
关键字:

微信app支付