微信Native支付简单实现

微信Native支付简单实现

因公司需求需要在网页上做一个扫码支付的功能,首先介绍一下基本的流程

1.用户在前台点击下单,此时订单状态为未支付,

2.之后订单的一系列处理之后,会在前端跳出一个二维码,提示用户扫码支付

3.生成二维码之后订单状态修改为支付中

4.用户成功扫码支付之后,微信会向我们的项目发送一个用户支付成功的请求

5.我们接收到请求把订单状态修改为已支付

微信支付提供的几种对接方式

基于上面的需求分析,文章主要介绍的是对接Native方式

Native方式

官方说明:

​ Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站、实体店单品或订单、媒体广告支付等场景.开发文档

解析: 商户系统指的就是我们自己的项目,按照微信支付协议生成支付二维码 那么微信支付协议是什么不清楚,暂缓,往下看,用户扫一扫支付简单,是基本的流程,适用于PC网站、实体店单品或订单、媒体广告支付等场景,那么这些场景都是以什么方式支付的呢,PC网站,比如在网上购买一个商品,选择微信或支付包支付会跳出一个二维码,扫码之后就是直接支付,不需要我们手动填写金额之类的,支付成功之后会告诉你一个支付中或者支付成功或失败的信息

JSAPI支付

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

应用场景有:

线下场所:调用接口生成二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付

公众号场景:用户在微信公众账号内进入商家公众号,打开某个主页面,完成支付

PC网站场景:在网站中展示二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付

解析:乍一看和上面的Native方式好像没区别都是调用接口生成二维码,只是说明了在微信浏览器中打开页面完成支付,具体的可以研究一下,目前我也不是很清楚

APP支付

APP支付是指商户通过在移动端应用APP中集成开放SDK调起微信支付模块来完成支付。适用于在移动端APP中集成微信支付功能的场景。开发文档

这个说的很明白了,移动端应用,不存在二维码,直接调用微信,就像手机上选择微信支付之后会直接跳到微信开始支付流程

H5支付

H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付。主要用于触屏版的手机浏览器请求微信支付的场景。可以方便的从外部浏览器唤起微信支付。开发文档

小程序支付

小程序支付是指商户通过调用微信支付小程序支付接口,在微信小程序平台内实现支付功能;用户打开商家助手小程序下单,输入支付密码并完成支付后,返回商家小程序。开发文档

正文开始

首先说明微信沙箱环境也需要提交乱七八糟的很多东西申请,而且听说有很多坑,建议直接使用正式环境

不像支付宝个人账号也可以使用沙箱环境

Native方式需要三个主要的东西

mchId:就是登录的时候的账号

AppID:登录进去再产品中心—>开发配置中查看

​ 需要注意的是这个APPID需要和微信公众号绑定,不然调接口会报一个Appid和密钥不匹配的错,这个请自行百度绑定方式

ApiKey:登录进去在账户中心—>API安全里面配置 注意这个只要是32位的字符就可以,可以手动输入,也可以使用工具生成 ,第一次设置需要设置一个操作密码还是什么的,设置一下就可以了,设置成功之后在使用这个密码去设置APIKey

在线生成地址:http://suijimimashengcheng.51240.com 或 http://www.sexauth.com/

微信商户平台 :https://pay.weixin.qq.com

在这里插入图片描述
在这里插入图片描述

二维码生成

​ 上面说到了Native支付必须按照微信支付协议生成二维码,那么二维码到底是个什么东西呢,简单介绍一下,码有很多种,像零食上面的条码是一种一维码,里面放不了很多东西,反正后来就出现了二维码,里面可以包含很多信息,可以用java的面向对象思想理解,二维码就是一个对象,里面有很多的属性值,那么我们是不是能在二维码里面放自己想放的东西呢,当然是可以的,我使用的是谷歌的一款二维码生成框架

<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>3.3.0</version>
</dependency>
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>javase</artifactId>
    <version>3.3.0</version>
</dependency>
public class QrCodeTest {
    public static void main(String[] args) {

        try {
            //二维码中的内容,随便设置
            String content = "大象哥二维码测试";
            //对二维码属性封装的一个对象
            MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
            Map hints = new HashMap();
            //设置二维码的编码
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
            //内容,生成二维码的方式使用QR,还有其他的可以自行研究
            // 市场常用的就是这个,图片的尺寸那些,最后就是二维码的编码了, 还有一些其他的可以研究一下
            BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 400, 400, hints);
            File file = new File("C:/Users/62601/Desktop/zssjlog/qr.jpg");
            //调用工具类把二维码生成在本地,正式项目肯定不是保存在本地
            //回通过流的方式输出给前台
            MatrixToImageWriter.writeToFile(bitMatrix, "jpg", file);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

生成的二维码

在这里插入图片描述

扫描之后会发现叫你复制去浏览器打开,这是为啥??? 没有按照微信的二维码规则生成的二维码,微信客户端肯定解析不了这个二维码啊,去浏览器打开,就能看到我们之前设置进去的值的

​ ok,现在我们知道了怎么生成二维码,就只有一个问题了,微信的二维码规则是什么,更具体的就是微信支付的二维码规则是什么,好了,这个问题不需要我们去了解,具体的微信肯定很复杂,不是我们这种小渣渣可以理解的

其实我们只需要向微信统一下单接口发送请求,他会给你返回一个字符串,使用这个字符串生成的二维码就是按照了微信二维码规则的

说明一下统一下单接口,就是一个接口的公网路径,我们需要使用工具向微信发送请求,后台发送请求的方式就不说了,接口需要的请求参数需要时XML格式的字符串,这个也比较麻烦,而且需要参数按照 啊斯克码表(asl…) 排序,使用TreeMap就可以了,默认就是那么排序的

代码示例:源码才是做好的API

为了方便,把所有代码都放在controller

/**
 * @author
 */
@Slf4j
@RestController
@RequestMapping("/wx")
public class WeixinPayController {
    private static final int BLACK = 0xff000000;
    private static final int WHITE = 0xFFFFFFFF;

    /**
     * pc端微信支付之后的回调方法
     *
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping("/notify")
    public void wechatNotifyUrlPc(HttpServletRequest request, HttpServletResponse response) throws Exception {
    }


    //微信支付接口
    @GetMapping("/pay")
    public void wxPay(HttpServletResponse response) throws Exception {
        WeChatParams ps = new WeChatParams();
        ps.setBody("商品描述");
        //商品价格,注意是以分为单位,后面不能有小数点
        // 测试用一分
        ps.setTotal_fee("1");
        //自己项目的订单ID
        ps.setOut_trade_no("123");
        //附加参数,随便设置
        ps.setAttach("111");
        //调用微信统一下单API返回二维码的code
        /**
         * 账号信息
         */
        String appid = WeChatProperties.appId;//微信服务号的appid
        String mch_id = WeChatProperties.mchId; //微信支付商户号
        String key = WeChatProperties.apiKey; // 微信支付的API密钥
        String notify_url = WeChatProperties.weChat_notify_url_pc;//回调地址【注意,这里必须要使用外网的地址】
        String ufdoder_url = WeChatProperties.ufdoderUrl;//微信下单API地址
        String tradeType = WeChatProperties.tradeType;//微信下单API地址

        /**
         * 时间字符串
         */
        String currTime = PayForUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayForUtil.buildRandom(4) + "";
        String nonce_str = strTime + strRandom;

        /**
         * 参数封装
         */
        SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);//随机字符串
        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", notify_url);//回调地址就是上面的路径
        packageParams.put("trade_type", tradeType);//支付方式
        packageParams.put("limit_pay", "no_credit");//限制用户不能使用信用卡
        packageParams.put("device_info", "WEB");//设备号
        packageParams.put("attach", ps.getAttach());//额外的参数【业务类型+会员ID+支付类型】


        String sign = PayForUtil.createSign("UTF-8", packageParams, key);  //获取签名
        packageParams.put("sign", sign);

        String requestXML = PayForUtil.getRequestXml(packageParams);//将请求参数转换成String类型
        log.info("微信支付请求参数的报文" + requestXML);
        String resXml = HttpUtil.postData(ufdoder_url, requestXML);  //解析请求之后的xml参数并且转换成String类型
        Map map = XMLUtil.doXMLParse(resXml);
        log.info("微信支付响应参数的报文" + resXml);
        String result;
        if (map.get("return_code").equals("SUCCESS") && map.get("result_code").equals("SUCCESS")) {
            result = (String) map.get("code_url");
        } else {
            throw new RuntimeException(map.get("err_code_des").toString());
        }

        String urlCode = getCodeUrl(ps);
        //生成二维码输出给前台
        //encodeQrcode(urlCode, response);

        /*
        //测试把二维码输出到本地,正式环境通过流给前端
        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);*/
    }

    public String getCodeUrl(WeChatParams ps) throws Exception {
        /**
         * 账号信息
         */
        String appid = WeChatProperties.appId;//微信服务号的appid
        String mch_id = WeChatProperties.mchId; //微信支付商户号
        String key = WeChatProperties.apiKey; // 微信支付的API密钥
        String notify_url = WeChatProperties.weChat_notify_url_pc;//回调地址【注意,这里必须要使用外网的地址】
        String ufdoder_url = WeChatProperties.ufdoderUrl;//微信下单API地址
        String tradeType = WeChatProperties.tradeType;//微信下单API地址

        /**
         * 时间字符串
         */
        String currTime = PayForUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayForUtil.buildRandom(4) + "";
        String nonce_str = strTime + strRandom;

        /**
         * 参数封装
         */
        SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);//随机字符串
        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", notify_url);//回调地址
        packageParams.put("trade_type", tradeType);//支付方式
        packageParams.put("limit_pay", "no_credit");//限制用户不能使用信用卡
        packageParams.put("device_info", "WEB");//设备号
        packageParams.put("attach", ps.getAttach());//额外的参数【业务类型+会员ID+支付类型】


        String sign = PayForUtil.createSign("UTF-8", packageParams, key);  //获取签名
        packageParams.put("sign", sign);

        String requestXML = PayForUtil.getRequestXml(packageParams);//将请求参数转换成String类型
        log.info("微信支付请求参数的报文" + requestXML);
        String resXml = HttpUtil.postData(ufdoder_url, requestXML);  //解析请求之后的xml参数并且转换成String类型
        Map map = XMLUtil.doXMLParse(resXml);
        log.info("微信支付响应参数的报文" + resXml);
        String result;
        if (map.get("return_code").equals("SUCCESS") && map.get("result_code").equals("SUCCESS")) {
            result = (String) map.get("code_url");
        } else {
            throw new RuntimeException(map.get("err_code_des").toString());
        }
        return result;
    }

    /**
     * 将路径生成二维码图片
     *
     * @param content
     * @param response
     * @author pzh
     */
    @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();
        }

    }

    /**
     * 类型转换
     *
     * @param matrix
     * @return
     * @author pzh
     */
    public 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;
    }


}

其他工具类都在码云上面,可以自行下载

链接: https://gitee.com/huang_jia_jun/WXPay-Native/tree/master

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用微信支付Java SDK来实现微信支付功能。以下是一个简单的示例代码: ```java import com.github.wxpay.sdk.WXPay; import com.github.wxpay.sdk.WXPayConfig; import com.github.wxpay.sdk.WXPayUtil; import java.util.HashMap; import java.util.Map; public class WeChatPayExample { public static void main(String[] args) { try { // 创建一个微信支付配置对象,配置相关参数 WXPayConfig config = new MyWXPayConfig(); // 创建一个微信支付对象 WXPay wxPay = new WXPay(config); // 构建请求参数 Map<String, String> data = new HashMap<>(); data.put("body", "商品描述"); data.put("out_trade_no", "商户订单号"); data.put("total_fee", "订单金额(单位:分)"); data.put("spbill_create_ip", "终端IP"); data.put("notify_url", "异步通知地址"); data.put("trade_type", "NATIVE"); // 调用统一下单API,获取支付二维码链接 Map<String, String> response = wxPay.unifiedOrder(data); // 处理返回结果 if ("SUCCESS".equals(response.get("return_code"))) { if ("SUCCESS".equals(response.get("result_code"))) { String codeUrl = response.get("code_url"); // 支付二维码链接 System.out.println("支付二维码链接:" + codeUrl); } else { System.out.println("下单失败,错误信息:" + response.get("err_code_des")); } } else { System.out.println("请求失败,错误信息:" + response.get("return_msg")); } } catch (Exception e) { e.printStackTrace(); } } } ``` 上述代码中的`MyWXPayConfig`是自定义的微信支付配置类,需要根据实际情况进行实现,配置相关参数,如`appid`、`mch_id`、`key`等。 注意:以上代码仅为示例,实际使用时需要根据自己的业务逻辑进行调整和完善。同时,为了保证支付安全性,建议将微信支付相关接口调用放在服务器端进行,避免将敏感信息泄露给客户端。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值