Java实现微信支付之Native模式

前言

微信支付以前就听说过,身边的同事也有弄过,但是自己因为没有遇到相关业务因此也没有去研究过。最近工作上可能会遇到微信支付因此也进行了些许研究,只是做到了接口掉通而已,并没有太深入,对微信支付已经很熟悉的同学请绕道走。

微信支付你需要了解内容

1.微信支付常用支付模式

JSAPI支付

JSAPI支付是指商户通过调用微信支付提供的JSAPI接口,在支付场景中调起微信支付模块完成收款。

APP支付

APP支付是指商户通过在移动端应用APP中集成开放SDK调起微信支付模块来完成支付。目前微信支付支持手机系统有:IOS(苹果)、Android(安卓)和WP(Windows Phone)

Native支付

Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。

小程序支付

商户已有微信小程序,用户通过好友分享或扫描二维码在微信内打开小程序时,可以调用微信支付完成下单购买的流程。

2.各种模式使用场景
支付模式使用场景
JSAPI支付线下场所、公众号场景和PC网站场景。
APP支付APP支付适用于在移动端APP中集成微信支付功能的场景
Native支付Native支付适用于PC网站、实体店单品或订单、媒体广告支付等场景
小程序支付小程序中唤起支付界面
付款码支付-----
3.个人理解(C端-用户,B端-商家)

JSAPI支付:C端使用B端程序,B端直接拉起支付界面,由C端完成支付,如:微信支付中手机话费充值
APP支付:C端使用B端开发的APP时,在APP中拉起支付界面
Native支付:由B端生成一个支付二维码,然后C端进行扫码拉起支付界面支付,如:菜市场买菜时微信支付
小程序支付:和APP支付一个道理

4.支付接口联调准备参数

由于支付涉及到商家,因此必须搞到商家相关的几个参数(自己注册成为商家是需要营业执照的,因此各位同学自己想办法吧),我测试的时候是直接用的别的企业的相关参数,所以不能共享出来的,所需参数内容,如下:

参数名称参数讲解
APPID由微信生成的应用ID,全局唯一。
商户号直连商户的商户号,由微信支付生成并下发。
支付密钥由商户提供用于进行验证支付
回调函数由商户提供一个可外网访问的接口用于接收支付结果(不可带参数)
商户订单号商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
5.支付接口认识

微信支付其实对每个支付模式都提供了一套完整的api接口,每个支付模式在官网都有非常详情的Api文档,此处我研究的是Native支付,因此提供一个Native的文档参考地址。

提示:微信支付并不是我们所想的简单的调用一下接口就OK了,比如Native支付是需要我们先调用下单接口(自己指定一个不重复的订单号),生成一个订单并返回二维码链接,用户扫码支付完成后,微信会自动调用我们提供的回调地址,将支付结果反馈给我们。我们下单生成的订单也是有用处的,比如进行支付的订单查询,退款等等

接口调用

此处我只实现了Native方式中的下单和回调函数的开发,至于其他接口也是同样的道理,大家可以举一反三。

1.下单接口
@GetMapping("/pay/{orderId}")
    public void wxPay(@PathVariable("orderId")String orderId, HttpServletResponse response) throws Exception {
        // 产品相关参数
        WeChatParams ps = new WeChatParams();
        ps.setBody("商品描述");
        // 商品价格,注意是以分为单位,后面不能有小数点
        ps.setTotal_fee("1");
        // 订单ID
        ps.setOut_trade_no(orderId);
        // 附加参数,随便设置
        ps.setAttach(UUID.randomUUID().toString().replace("-",""));

        /**构造请求参数*/
        SortedMap<Object, Object> packageParams = new TreeMap<>();
        packageParams.put("appid", WeChatProperties.appId);
        packageParams.put("mch_id", WeChatProperties.mchId);
        // 随机字符串 【备注:每次发起请求都需要随机的字符串,否则失败。】
        packageParams.put("nonce_str", UUID.randomUUID().toString().replace("-",""));
        // 支付的商品名称
        packageParams.put("body", ps.getBody());
        // 商户订单号
        packageParams.put("out_trade_no", ps.getOut_trade_no());
        // //支付金额
        packageParams.put("total_fee", ps.getTotal_fee());
        // 客户端主机
        packageParams.put("spbill_create_ip", PayForUtil.localIp());
        // 回调地址
        packageParams.put("notify_url", WeChatProperties.weChat_notify_url_pc);
        // 支付方式
        packageParams.put("trade_type", WeChatProperties.tradeType);
        // 限制用户不能使用信用卡
        packageParams.put("limit_pay", "no_credit");
        // 设备号
        packageParams.put("device_info", "WEB");
        // //额外的参数
        packageParams.put("attach", ps.getAttach());
        // 设置签名
        String sign = PayForUtil.createSign("UTF-8", packageParams, WeChatProperties.apiKey);
        packageParams.put("sign", sign);
        String requestXML = PayForUtil.getRequestXml(packageParams);
        System.out.println("支付下单接口请求参数:" + requestXML);

        /**调用微信统一下单API返回二维码的code*/
        String resXml = HttpUtil.postData(WeChatProperties.ufdoderUrl, requestXML);
        Map map = XMLUtil.doXMLParse(resXml);
        System.out.println("微信下单接口响应结果:" + resXml);
        if (map.get("return_code").equals("SUCCESS") && map.get("result_code").equals("SUCCESS")) {
            // 二维码地址
            String result = (String) map.get("code_url");
            //生成二维码输出给前台
            encodeQrcode(result, response);
        } else {
            throw new RuntimeException(map.get("err_code_des").toString());
        }
        /*
        //测试把二维码输出到本地,正式环境通过流给前端
        HashMap<EncodeHintType, String> map = new HashMap<>();
        map.put(EncodeHintType.CHARACTER_SET, "utf-8");
        BitMatrix bitMatrix;
        bitMatrix = new MultiFormatWriter().encode(urlCode, BarcodeFormat.QR_CODE, 250, 250,map );
        StringBuilder path =new StringBuilder();
        path.append("C:\\Users\\Administrator\\Desktop\\demo\\");
        path.append("wx22.jpg");
        Path file = new File(path.toString()).toPath();
        MatrixToImageWriter.writeToPath(bitMatrix, "jpg", file);*/
    }
2.将地址生成二维码工具方法
@SuppressWarnings({"unchecked", "rawtypes"})
    public static void encodeQrcode(String content, HttpServletResponse response) {
        if (StringUtils.isBlank(content)) {
            return;
        }
        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        Hashtable hints = new Hashtable();
        //设置容错级别最高
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        //设置字符编码为utf-8
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        //二维码空白区域,最小为0也有白边,只是很小,最小是6像素左右
        hints.put(EncodeHintType.MARGIN, 1);
        BitMatrix bitMatrix = null;
        try {
            bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 250, 250, hints);
            BufferedImage image = toBufferedImage(bitMatrix);
            //输出二维码图片流
            try {
                response.setHeader("Content-Type", "image/jpeg");
                ImageIO.write(image, "png", response.getOutputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (WriterException e1) {
            e1.printStackTrace();
        }
    }

    private static BufferedImage toBufferedImage(BitMatrix matrix) {
        int width = matrix.getWidth();
        int height = matrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, matrix.get(x, y) == true ? BLACK : WHITE);
            }
        }
        return image;
    }
3.回调方法
@RequestMapping("/notify")
    public void wechatNotifyUrlPc(HttpServletRequest request, HttpServletResponse response) throws Exception {
        BufferedReader reader = request.getReader();
        StringBuffer buffer = new StringBuffer();
        String line = "";
        while ((line = reader.readLine()) != null) {
            buffer.append(line);
        }
        String xmlString = buffer.toString();
        request.getReader().close();
        // 验证签名前不要修改reqParam中的键值对的内容,否则会验签不过
        if (!WXPayUtil.isSignatureValid(xmlString, WeChatProperties.apiKey)) {
            System.out.println("验证签名结果[失败].");
        } else {
            Map<String, String> map = XMLUtil.doXMLParse(xmlString);
            String resultCode = map.get("result_code");
            if ("SUCCESS".equalsIgnoreCase(resultCode)) {
                // TODO 支付成功,做后续业务逻辑处理
                System.out.println("支付成功,请继续作业");

            } else {
                // TODO 失败,做失败业务逻辑处理
                System.out.println("支付失败,请排查原因");
            }
        }
        // 响应微信端,阻止其持续调用
        response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
    }

小结

本文只是简单的贴出来核心的代码,并不是很全面,需要全面的代码的同学请移步到码云下载我的源码哈。本文的内容,经过清测,是可以调用并接收通知的,请大家放心使用。
源码地址:https://gitee.com/zhaobolan/wechat_pay_native

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值