黑马程序员技术交流社区

标题: 【上海校区】Java实现微信支付 [打印本页]

作者: 郑委员    时间: 2019-3-10 15:02
标题: 【上海校区】Java实现微信支付
本帖最后由 郑委员 于 2019-3-10 21:55 编辑

Java实现微信支付



一、业务需求

  实现app微信支付,后端需要做生成预支付单,响应支付结果。

二、业务流程

  查看官方文档

  https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_3;

三、实现方法

  3.1 创建微信工具类  ConstantUtil
[Java] 纯文本查看 复制代码
public class ConstantUtil {
    /**
     * 微信开发平台应用ID
     */
    public static final String APP_ID="wx2421b1c4370ec43b";
    /**
     * 应用对应的凭证
     */
    public static final String APP_SECRET="1add1a30ac87aa2db72f57a2375d8fec";
    /**
     * 应用对应的密钥
     */
    public static final String APP_KEY="1add1a30ac87aa2db72f57a2375d8fec";
    /**
     * 微信支付商户号
     */
    public static final String MCH_ID="10000100";
    /**
     * 商品描述
     */
    public static final String BODY="充值";
    /**
     * 商户号对应的密钥
     */
    public static final String PARTNER_key="*******";

    /**
     * 商户id
     */
    public static final String PARTNER_ID="*******";
    /**
     * 常量固定值
     */
    public static final String GRANT_TYPE="client_credential";
    /**
     * 获取预支付id的接口url
     */
    public static String GATEURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    /**
     * 微信服务器回调通知url
     */
    public static String NOTIFY_URL="http://url"; //可以访问的url
    /**
     * 微信服务器查询订单url
     */
    public static String ORDER_QUERY="https://api.mch.weixin.qq.com/pay/orderquery";

}

  3.2 生成预支付订单
[Java] 纯文本查看 复制代码
Map<String, Object> getOrder(@RequestParam(value = "totalFee") String totalFee, @RequestParam(value = "deviceInfo") String deviceInfo,
                                 @RequestParam(value = "attach") String attach,
                                 HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        // 获取生成预支付订单的请求类
        PrepayIdRequestHandler prepayReqHandler = new PrepayIdRequestHandler(request, response);
//        String totalFee = request.getParameter("total_fee");
        int total_fee = (int) (Float.valueOf(totalFee) * 100);
        System.out.println("total:" + total_fee);
        System.out.println("total_fee:" + total_fee);
        prepayReqHandler.setParameter("appid", ConstantUtil.APP_ID);
        prepayReqHandler.setParameter("body", ConstantUtil.BODY);
        prepayReqHandler.setParameter("mch_id", ConstantUtil.MCH_ID);
        prepayReqHandler.setParameter("device_info", deviceInfo); //卡号
        prepayReqHandler.setParameter("attach", attach);//套餐值
        String nonce_str = WXUtil.getNonceStr();
        prepayReqHandler.setParameter("nonce_str", nonce_str);
        prepayReqHandler.setParameter("notify_url", ConstantUtil.NOTIFY_URL);
        out_trade_no = String.valueOf(UUID.next());
        prepayReqHandler.setParameter("out_trade_no", out_trade_no);
        prepayReqHandler.setParameter("spbill_create_ip", request.getRemoteAddr());
        String timestamp = WXUtil.getTimeStamp();
        prepayReqHandler.setParameter("time_start", timestamp);
        System.out.println(String.valueOf(total_fee));
        prepayReqHandler.setParameter("total_fee", String.valueOf(total_fee));
        prepayReqHandler.setParameter("trade_type", "APP");
        /**
         * 注意签名(sign)的生成方式,具体见官方文档(传参都要参与生成签名,且参数名按照字典序排序,最后接上APP_KEY,转化成大写)
         */
        prepayReqHandler.setParameter("sign", prepayReqHandler.createMD5Sign());
        prepayReqHandler.setGateUrl(ConstantUtil.GATEURL);
        String prepayid = prepayReqHandler.sendPrepay();
        // 若获取prepayid成功,将相关信息返回客户端
        if (prepayid != null && !prepayid.equals("")) {
            String signs = "appid=" + ConstantUtil.APP_ID + "&noncestr=" + nonce_str + "&package=Sign=WXPay&partnerid="
                    + ConstantUtil.PARTNER_ID + "&prepayid=" + prepayid + "×tamp=" + timestamp + "&key="
                    + ConstantUtil.APP_KEY;
            map.put("code", 0);
            map.put("info", "success");
            map.put("prepayid", prepayid);
            /**
             * 签名方式与上面类似
             */
            map.put("sign", MD5Util.MD5Encode(signs, "utf8").toUpperCase());
            map.put("appid", ConstantUtil.APP_ID);
            map.put("device_info", deviceInfo);
            map.put("attach", attach);
            map.put("timestamp", timestamp);  //等于请求prepayId时的time_start
            map.put("noncestr", nonce_str);   //与请求prepayId时值一致
            map.put("package", "Sign=WXPay");  //固定常量
            map.put("partnerid", ConstantUtil.PARTNER_ID);
        } else {
            map.put("code", 1);
            map.put("info", "获取prepayid失败");
        }
        return map;
    }


  3.3  异步通知客户结果


[Java] 纯文本查看 复制代码
PrintWriter writer = response.getWriter();
        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        String result = new String(outSteam.toByteArray(), "utf-8");
        System.out.println("微信支付通知结果:" + result);
        Map<String, String> map = null;
        try {
            /**
             * 解析微信通知返回的信息
             */
            map = XMLUtil.doXMLParse(result);
        } catch (JDOMException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


   官方文档中特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。
   支付结果通知内容做签名验证。



[Java] 纯文本查看 复制代码
//支付成功后sign校验
String appid=map.get("appid");
String transaction_id=map.get("transaction_id");
String nonce_str =map.get("nonce_str");
String bank_type =map.get("bank_type");
String openid=map.get("openid");
String sign =map.get("sign");
String fee_type =map.get("fee_type");
String mch_id =map.get("mch_id");
String cash_fee =map.get("cash_fee");
String device_info =map.get("device_info");
String out_trade_no =map.get("out_trade_no");
String total_fee =map.get("total_fee");
String trade_type =map.get("trade_type");
String result_code =map.get("result_code");
String attach =map.get("attach");
String time_end =map.get("time_end");
String is_subscribe =map.get("is_subscribe");
String return_code=map.get("return_code");

PrepayIdRequestHandler prepayReqHandler = new PrepayIdRequestHandler(request, response);
prepayReqHandler.setParameter("appid",appid);
prepayReqHandler.setParameter("attach",attach);
prepayReqHandler.setParameter("bank_type",bank_type);
prepayReqHandler.setParameter("cash_fee",cash_fee);
prepayReqHandler.setParameter("device_info",device_info);
prepayReqHandler.setParameter("fee_type",fee_type);
prepayReqHandler.setParameter("is_subscribe",is_subscribe);
prepayReqHandler.setParameter("mch_id",mch_id);
prepayReqHandler.setParameter("nonce_str",nonce_str);
prepayReqHandler.setParameter("openid",openid);
prepayReqHandler.setParameter("out_trade_no",out_trade_no);
prepayReqHandler.setParameter("result_code",result_code);
prepayReqHandler.setParameter("return_code",return_code);
prepayReqHandler.setParameter("time_end",time_end);
prepayReqHandler.setParameter("total_fee",total_fee);
prepayReqHandler.setParameter("trade_type",trade_type);
prepayReqHandler.setParameter("transaction_id",transaction_id);

String endSigns =prepayReqHandler.createMD5Sign(); //支付结果通知的内容 签名校验


   商户金额的查询


[Java] 纯文本查看 复制代码
PrepayIdRequestHandler orderQueryHandler = new PrepayIdRequestHandler(request, response);
        orderQueryHandler.setParameter("appid",appid);
        orderQueryHandler.setParameter("mch_id",mch_id);
        orderQueryHandler.setParameter("out_trade_no",out_trade_no);
        orderQueryHandler.setParameter("nonce_str",nonce_str);
        orderQueryHandler.setParameter("sign",orderQueryHandler.createMD5Sign());
        orderQueryHandler.setGateUrl(ConstantUtil.ORDER_QUERY);
        String resXml = "";
        WeixinOrderBean orderBean =orderQueryHandler.selectOrder();


  应答微信 


[AppleScript] 纯文本查看 复制代码
if(sign.equals(endSigns) && Integer.parseInt(total_fee)==orderBean.getTotal_fee()){ // 签名通过并且返回的订单的金额与商户金额相同
            // 若支付成功,则告知微信服务器收到通知
            if (map.get("return_code").equals("SUCCESS")) {
                System.out.println("充值成功!");
                //PayRecord payRecord=payRecordService.get(Long.valueOf(map.get("out_trade_no")));
                System.out.println("订单号:" + Long.valueOf(map.get("out_trade_no")));
                String iccids = map.get("device_info");
                String monthNum = map.get("attach");
          //根据自己的业务处理
                int count =trafficCardListService.selectExistTrafficCardByOutTradeNo(map.get("out_trade_no")); //查询商户订单号是否存在
                if(count==0){//不存在就插入
                    List<MsisdnOrderBean> msisdnOrderList = trafficCardListService.selectTrafficCardRechargeResult(iccids, monthNum);
                    if (msisdnOrderList.size() > 0) {
                        for (MsisdnOrderBean msisdnOrderBean : msisdnOrderList) {
                            msisdnOrderBean.setOutTradeNo(map.get("out_trade_no"));
                            msisdnOrderBean.setDate(map.get("time_end"));
                            msisdnOrderBean.setIccids(map.get("device_info"));
                            // msisdnOrderBean.setTotalFee(Integer.parseInt(map.get("total_fee")));
                            msisdnOrderBean.setTotalFee(BigDecimal.valueOf(Integer.parseInt(map.get("total_fee"))).divide(new BigDecimal(100)).doubleValue());
                            msisdnOrderBean.setTransactionId(map.get("transaction_id"));
                            msisdnOrderBean.setTrafficNo(DateUtils.getNowDataTime() + ApiUtils.getFourRandom()); //年月日时分秒+四位随机数
                            trafficCardListService.insertTrafficCardList(msisdnOrderBean);
                        }
                    }
                }
                resXml = XMLUtil.setXML("SUCCESS", "OK");
            } else {
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[通知签名验证失败]]></return_msg>" + "</xml> ";
                logger.info("通知签名验证失败");
            }
        }else{
            resXml =XMLUtil.setXML("FAIL", "签名不一致");
        }

        writer.write(resXml);
        writer.flush();
        writer.close();


  运用到的 PrepayIdRequestHandler  工具类


[Java] 纯文本查看 复制代码
public class PrepayIdRequestHandler extends RequestHandler {

    public PrepayIdRequestHandler(HttpServletRequest request,
                                  HttpServletResponse response) {
        super(request, response);
    }

    public String createMD5Sign() {
        StringBuffer sb = new StringBuffer();
        Set es = super.getAllParameters().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();
            sb.append(k + "=" + v + "&");
        }

        String params=sb.append("key="+ConstantUtil.APP_KEY).substring(0);
        String sign = MD5Util.MD5Encode(params, "utf8");
        return sign.toUpperCase();
    }

    // 提交预支付
    public String sendPrepay() throws Exception {
        String prepayid = "";
        Set es=super.getAllParameters().entrySet();
        Iterator it=es.iterator();
        StringBuffer sb = new StringBuffer("<xml>");
        while(it.hasNext()){
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            sb.append("<"+k+">"+v+"</"+k+">");
        }
        sb.append("</xml>");
        String params=sb.substring(0);
        System.out.println("请求参数:"+params);
        String requestUrl = super.getGateUrl();
        System.out.println("请求url:"+requestUrl);
        TenpayHttpClient httpClient = new TenpayHttpClient();
        httpClient.setReqContent(requestUrl);
        String resContent = "";
        if (httpClient.callHttpPost(requestUrl, params)) {
            resContent = httpClient.getResContent();
            System.out.println("获取prepayid的返回值:"+resContent);
            Map<String,String> map=XMLUtil.doXMLParse(resContent);
            if(map.containsKey("prepay_id"))
                prepayid=map.get("prepay_id");
        }
        return prepayid;
    }

    // 查询订单
    public WeixinOrderBean selectOrder() throws Exception {
        Set es=super.getAllParameters().entrySet();
        Iterator it=es.iterator();
        StringBuffer sb = new StringBuffer("<xml>");
        while(it.hasNext()){
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            sb.append("<"+k+">"+v+"</"+k+">");
        }
        sb.append("</xml>");
        String params=sb.substring(0);
        System.out.println("请求参数:"+params);
        String requestUrl = super.getGateUrl();
        System.out.println("请求url:"+requestUrl);
        TenpayHttpClient httpClient = new TenpayHttpClient();
        httpClient.setReqContent(requestUrl);
        String resContent = "";
        WeixinOrderBean orderBean =new WeixinOrderBean();
        if (httpClient.callHttpPost(requestUrl, params)) {
            resContent = httpClient.getResContent();
            Map<String,String> map=XMLUtil.doXMLParse(resContent);
            if(map.size()>0){
                orderBean.setAppid(map.get("appid"));
                orderBean.setAttach(map.get("attach"));
                orderBean.setDevice_info(map.get("device_info"));
                orderBean.setBank_type(map.get("bank_type"));
                orderBean.setMch_id(map.get("mch_id"));
                orderBean.setNonce_str(map.get("nonce_str"));
                orderBean.setTotal_fee(Integer.parseInt(map.get("total_fee")));
                orderBean.setTrade_state(map.get("trade_state"));
            }
        }
        return orderBean;
    }
}








欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2