首页 > 编程语言 >Java实现微信扫码支付(NATIVE方式)[全网最简单]

Java实现微信扫码支付(NATIVE方式)[全网最简单]

时间:2022-10-21 14:44:10浏览次数:55  
标签:扫码 Java String get 微信 支付 public packageParams

  基本业务逻辑就是用户访问过来,我们去调微信支付的接口人家返给我们一个二维码我们丢给前端让用户扫码支付就行,等他支付完了微信会回调通知我们支付完了,这个回调的地址需要外网可以访问到,这里用到Ngrok的内网穿透,先看我操作。

导入依赖:

        <!-- hutool -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.2</version>
        </dependency>  
        <!-- 微信支付 SDK -->
        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>
        <!-- 二维码 -->
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.5.0</version>
        </dependency>
        <!-- wechatpay httpclient -->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.8</version>
        </dependency>     

hutool的工具包,好多工具方法免得自己写了,包含了大部分我们想用的工具类,发起HTTP请求、生成二维码,解析xml转map等,有兴趣可以自己研究。  

第一步:参数配置类

基本的参数需要去微信网站申请,不多赘述,直接上代码

public class WeChatConfig {
 
	/**
	* 微信服务号APPID
	*/
	public static String APPID="wx6b4656gd30cb1c8c";
	/**
	* 微信支付的商户号
	*/
	public static String MCHID="16132344586";
	/**
	* 微信支付的API密钥
	*/
	public static String APIKEY="Dafawangluokejiyouxiaewqeongsi1234";
	/**
	* 微信支付成功之后的回调地址【注意:当前回调地址必须是公网能够访问的地址】
      内网穿透去实现
	*/
	public static String WECHAT_NOTIFY_URL_PC="http://inxus.free.idcfengye.com/wxnotify";
	/**
	* 微信统一下单API地址
	*/
	public static String UFDODER_URL="https://api.mch.weixin.qq.com/pay/unifiedorder";
    /**
	* 应用对应的凭证
	*/
	public static String APP_SECRET="10a893a9d0964a8e76a6042d0650f08a";
}

第二步:基本的工具类 

验证签名、获取本地IP,生成订单号以及元转分(微信支付是以分为单位,0.1元即为10分)的一些方法 

public class PayForUtil {
 
    /**
     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     * @return boolean
     */
    public static boolean isTenpaySign( SortedMap<Object, Object> packageParams, String API_KEY) {
        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(!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + API_KEY);
 
        //算出摘要
        String mysign = DigestUtil.md5Hex(sb.toString()).toLowerCase();
        String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();
 
        return tenpaySign.equals(mysign);
    }
 
    /**
     * sign签名
     * @return String
     */
    public static String createSign( SortedMap<Object, Object> packageParams, String API_KEY) {
        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 = entry.getValue().toString();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + API_KEY);
        String sign = DigestUtil.md5Hex(sb.toString());
        return sign;
    }
 
    /**
     * 获取本机IP地址
     * @return String
     */
    public static String localIp(){
        String ip = null;
        Enumeration allNetInterfaces;
        try {
            allNetInterfaces = NetworkInterface.getNetworkInterfaces();
            while (allNetInterfaces.hasMoreElements()) {
                NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
                List<InterfaceAddress> InterfaceAddress = netInterface.getInterfaceAddresses();
                for (InterfaceAddress add : InterfaceAddress) {
                    InetAddress Ip = add.getAddress();
                    if (Ip != null && Ip instanceof Inet4Address) {
                        ip = Ip.getHostAddress();
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }
        return ip;
    }
 /**
     * 生成订单号
     * @return
     */
    public static String getOrderNo() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String newDate = sdf.format(new Date());
        String result = "";
        Random random = new Random();
        for (int i = 0; i < 3; i++) {
            result += random.nextInt(10);
        }
        return newDate + result;
    }
    /**
     * 元转换成分
     * @param amount
     * @return
     */
    public static String getMoney(String amount) {
        if(amount==null){
            return "";
        }
        // 金额转化为分为单位
        // 处理包含, ¥ 或者$的金额
        String currency =  amount.replaceAll("\\$|\\¥|\\,", "");
        int index = currency.indexOf(".");
        int length = currency.length();
        Long amLong = 0l;
        if(index == -1){
            amLong = Long.valueOf(currency+"00");
        }else if(length - index >= 3){
            amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));
        }else if(length - index == 2){
            amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);
        }else{
            amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");
        }
        return amLong.toString();
    }
}

 

第三步: 编写controller层

 
@Controller
public class PayController {
 
	//预支付接口(生成二维码并输出到浏览器)
    @GetMapping("codePay" )
    @ResponseBody
    private void codePay(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 支付的商品名称
        String body="可口可乐";
        //支付金额(单位:分)
        String totalFee="1";
 
        //获取二维码内容urlCode
        String resXml = WeChatPay.getNative( body, totalFee);
        Map<String, Object> data= XmlUtil.xmlToMap(resXml);
        String urlCode = data.get("code_url").toString();
 
        //生成二维码到输出流
        response.setContentType("image/jpeg");
        ServletOutputStream out = response.getOutputStream();
        QrCodeUtil.generate(urlCode, 300, 300,"jpg" ,out);
        out.close();
    }
 
	//回调接口
    @RequestMapping("wxnotify" )
    public void wxnotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.err.println("开始回调。。。");
        WeChatPay.notify(request, response);
    }
}

第四步:编写业务层

具体的封装参数向微信发起请求返回code_url即二维码,和回调的业务

public class WeChatPay {
 
	/**
	 * @Description NATIVE支付模式(二维码网页扫码支付)
	 */
	public static String getNative(String body,String totalFee) throws Exception {
		//账号信息
		String appid = WeChatConfig.APPID;
		String mch_id = WeChatConfig.MCHID;
		String key = WeChatConfig.APIKEY;
 
		//微信支付成功之后的回调地址【注意:当前回调地址必须是公网能够访问的地址】
		String notify_url = WeChatConfig.WECHAT_NOTIFY_URL_PC;
		String ufdoder_url = WeChatConfig.UFDODER_URL;
		String trade_type = "NATIVE";
		String out_trade_no = PayForUtil.getOrderNo();
 
		//请求参数封装
		SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
		packageParams.put("appid", appid);
		packageParams.put("mch_id", mch_id);
		// 随机字符串
		packageParams.put("nonce_str", "nonce"+out_trade_no);
		// 支付的商品名称
		packageParams.put("body", body);
		// 商户订单号【备注:每次发起请求都需要随机的字符串,否则失败。】
		packageParams.put("out_trade_no",out_trade_no);
		// 支付金额
		packageParams.put("total_fee", totalFee);
		// 客户端主机
		packageParams.put("spbill_create_ip", PayForUtil.localIp());
		packageParams.put("notify_url", notify_url);
		packageParams.put("trade_type", trade_type);
 
		// 获取签名
		String sign = PayForUtil.createSign(packageParams, key);
		packageParams.put("sign", sign);
 
		// 将请求参数转换成String类型
		String requestXML = XmlUtil.mapToXmlStr(packageParams, "xml");
		System.err.println("请求报文:-----------------------------------");
		System.err.println(requestXML);
 
		// 解析请求之后的xml参数并且转换成String类型
		String resXml= HttpUtil.post(ufdoder_url, requestXML);
		System.err.println("应答报文:-----------------------------------");
		System.err.println(resXml);
 
		return resXml;
	}
 
	/**
	 * @Description 支付回调
	 */
	public static void notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 读取回调数据
		InputStream inputStream;
		StringBuffer sb = new StringBuffer();
		inputStream = request.getInputStream();
		String s;
		BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
		while ((s = in.readLine()) != null) {
			sb.append(s);
		}
		in.close();
		inputStream.close();
 
		// 解析xml成map
		Map<String, Object> m=XmlUtil.xmlToMap(sb.toString());
		// 过滤空 设置 TreeMap
		SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
		Iterator<String> it = m.keySet().iterator();
		while (it.hasNext()) {
			String parameter = it.next();
			Object parameterValue = m.get(parameter);
			String v = "";
			if (null != parameterValue) {
				v = parameterValue.toString().trim();
			}
			packageParams.put(parameter, v);
		}
 
		// 微信支付的API密钥
		String key = WeChatConfig.APIKEY;
		// 判断签名是否正确
		if (PayForUtil.isTenpaySign(packageParams, key)) {
			String resXml = "";
			if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
				System.err.println("--------------------------------------------");
				System.err.println("支付回调成功。。。可在此处执行业务逻辑。。。");
				System.err.println("--------------------------------------------");
 
				// 支付成功,执行自己的业务逻辑开始
				String app_id = (String) packageParams.get("appid");
				String mch_id = (String) packageParams.get("mch_id");
				String openid = (String) packageParams.get("openid");
				// 是否关注公众号
				String is_subscribe = (String) packageParams.get("is_subscribe");
				// 附加参数【商标申请_0bda32824db44d6f9611f1047829fa3b_15460】--【业务类型_会员ID_订单号】
				String attach = (String) packageParams.get("attach");
				String out_trade_no = (String) packageParams.get("out_trade_no");
				String total_fee = (String) packageParams.get("total_fee");
				// 微信支付订单号
				String transaction_id = (String) packageParams.get("transaction_id");
				// 支付完成时间
				String time_end = (String) packageParams.get("time_end");
 
				// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
				resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
						+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
			} else {
				System.err.println("支付失败,错误信息:" + packageParams.get("err_code"));
 
				resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
						+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
			}
			response.getWriter().write(resXml);
		} else {
			System.err.println("通知签名验证失败");
		}
	}
}

运行项目,访问支付接口就可以看到生成的二维码了...

 请求成功,控制台打印如下:

至此微信扫码支付基本完成,包括反二维码的接口和微信回调的接口,目前回调接口是访问不到的,需要内网穿透,可以看我另一篇文章

标签:扫码,Java,String,get,微信,支付,public,packageParams
From: https://www.cnblogs.com/wffzk/p/16813433.html

相关文章

  • Java反射设置可以访问私有成员
    定义一个测试类publicclassUser{privateStringname="ppp";privateStringsex;privateintage;privatevoideat(Stringnum){Sy......
  • Java关键字(三)——static
    我们说Java是一种面向对象编程的语言,而对象是把数据及对数据的操作方法放在一起,作为一个相互依存的整体,对同类对象抽象出其共性,便是Java中的类,我们可以用类描述世间万物......
  • 微信小程序基于vant和springboot实现附件上传和预览
    前言图片上传和预览在移动端应用非常广泛和频繁,vant组件库van-uploader组件已经帮我们实现了大部分功能,但是在系统中频繁使用还是有点麻烦,我们根据自身的业务系统重新封装了......
  • NodeJS & Dapr Javascript SDK 官方使用指南
    Dapr是一个可移植的、事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的、无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言和开发框......
  • Java一个还不错的日期格式转换工具类(附源码)
    Java工具类pom依赖<commons-lang3.version>3.3.2</commons-lang3.version><dependency><groupId>org.apache.commons</groupId......
  • 【JavaWeb】 Mybatis-02-Mybatis的快速入门
    @[Toc]写在前言mybatis里面的配置文件是如何联系的这是我第二次学习mybatis的理解,我理解的和如下图,config包含了数据库的配置,以及mappers,通过mappers可以找到各类Mapper,这样......
  • 【JavaWeb】 Mybatis-03-Mybatis代理开发
    @[Toc]Mybatis代理1、为什么使用Mybatis代理?Mybatis开发者给了用户两个配置的方式,一个是如我们​​JavaWebMybatis-02-Mybatis的快速入门​​里的那样单纯使用XML配置,另外......
  • Java多线程实现的四种方式
    实现多线程有以下四种方式实现多线程有以下四种方式:1.继承Thread类2.实现Runnable接口3.实现Callable接口4.线程池:提供了一个线程队列,队列中保存着所有等待状态的线程......
  • java----util常用类,1字符串转日期,2格式化类Format,3Random,4MD5,5Base64
    util常用类1.字符串转日期1.1Date/CalendarStringdateStr="2020-01-0110:00:00";jdk1.8之前与时间日期相关的类型有2个 java.util.Date  java.util.Calen......
  • Java语言深入:深入研究Java equals方法,equals,==,equals用法
    网上关于equals和==的区别的讨论巨多这里先简单分析下他们的区别吧:equals方法(是String类从它的超类Object中继承的)被用来检测两个对象是否相等,即两个对象的内容是否相等......