微信支付的统一下单小demo

1. 生成二维码

1.导入zxing的依赖

<properties>
    <zxing.version>3.4.0</zxing.version>
</properties>
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>${zxing.version}</version>
</dependency>
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>javase</artifactId>
    <version>${zxing.version}</version>
</dependency>

2.ZxingUitl

import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.util.EnumMap;

import javax.imageio.ImageIO;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

public class ZXingCodeEncodeUtil {

	// 二维码颜色
	private static final int BLACK = 0xFF000000;
	
	// 二维码背景颜色
	private static final int WHITE = 0xFFFFFFFF;
	// 二维码格式参数
	private static final EnumMap<EncodeHintType, Object> hints = new EnumMap<EncodeHintType, Object>(EncodeHintType.class);
	
	static {
		/*
		 * 二维码的纠错级别(排错率),4个级别: L (7%)、 M (15%)、 Q (25%)、 H (30%)(最高H)
		 * 纠错信息同样存储在二维码中,纠错级别越高,纠错信息占用的空间越多,那么能存储的有用讯息就越少;共有四级; 选择M,扫描速度快。
		 */
		hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
		// 二维码边界空白大小 1,2,3,4 (4为默认,最大)
		hints.put(EncodeHintType.MARGIN, 1);
		hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");//设置放入的字符的编码
	}
	/**
	 * 创建一个二维码保存到d盘
	 */
	public static BufferedImage createZXingCodeSaveToDpan
        (String content, int width, int height, String path, String imageType) {
		// 
		try {
			BitMatrix encode = new MultiFormatWriter()
                .encode(content, BarcodeFormat.QR_CODE, width, height,hints);
			
			// 得到二维码的宽度
			int code_width = encode.getWidth();
			// 得到二维码的高度
			int code_height = encode.getHeight();
			
            
			// 创建一张图片
			BufferedImage bufferedImage = 
                new BufferedImage(code_width, code_height, BufferedImage.TYPE_INT_RGB);
			// 向图片中添加内容
			for(int i = 0; i < code_width; i++) {
				for(int j = 0; j < code_height; j++) {
					bufferedImage.setRGB(i, j, encode.get(i, j)?BLACK:WHITE);
				}
			}
			File file = new File(path);
			if(!file.exists()) {
				file.createNewFile();
			}
			ImageIO.write(bufferedImage, imageType, file);
			// 将图片保存在d盘
			
			System.out.println("二维码生成成功");
			return bufferedImage;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	
	
	/**
	 * 创建一个带LOGO的二维码
	 */
	public static void createZXingCodeWithLogoSaveToDpan(String content, int width, int height, String path, String imageType,InputStream logoStream) {
		try {
			// 获得二维码
			BufferedImage codeNormal  = createZXingCodeSaveToDpan(content, width, height, path, imageType);
			if(null != codeNormal) {
				if(null != logoStream) {
					// 获得二维码的宽高
					int code_width = codeNormal.getWidth();
					int code_height = codeNormal.getHeight();
					// 获得图片的宽高
					BufferedImage image = ImageIO.read(logoStream);
					int image_width = image.getWidth();
					int image_height = image.getHeight();
					// 计算初始坐标
					int x = (code_width - image_width) / 2;
					int y = (code_height - image_height) / 2;
					
					// 计算应画在二维码上的宽高
					int code_logo_width = ((code_width)/5) < image_width ? ((code_width)/5) : image_width;
					int code_logo_height = ((code_height)/5) < image_height ?  ((code_height)/5) : image_height;
					
					// 定义画笔
					
					Graphics2D graphics = codeNormal.createGraphics();
					graphics.setStroke(new BasicStroke(2));
					// 在二维码上画出图片
					graphics.drawImage(image, x, y, code_logo_width, code_logo_height, null);
					// 让画上去的内容生效
					graphics.dispose();
					// 将二维码保存到d盘
					
					File file = new File(path);
					if(!file.exists()) {
						file.createNewFile();
					}
					ImageIO.write(codeNormal, imageType, file);
				}
			}else {
				System.out.println("二维码生成失败");
			}
			
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		createZXingCodeSaveToDpan(
			"weixin://wxpay/bizpayurl?pr=yDRxFJ4", 200, 200, "d://order.jpg", "PNG"
		);
	}

	
}

3.输出到网页

@Controller
@RequestMapping("qr")
public class QrController {

    @RequestMapping("doOrder")
    public void doOrder(HttpServletRequest req, HttpServletResponse resp) throws IOException {

        BufferedImage bufferedImage = ZXingCodeEncodeUtil
           .createZXingCodeSaveToDpan("weixin://wxpay/bizpayurl?pr=yDRxFJ4", 400, 400, null, "PNG");
        ImageIO.write(bufferedImage, "PNG", resp.getOutputStream());
        
    }


}

总之:zxing的使用就是导入包后

利用MultiFormatWriter对象的encode方法,将content编码返回BitMatrix对象(矩阵信息),然后使用MatrixToImageWriter的静态方法toBufferedImage把矩阵信息转为BufferedImage信息。

2.网络穿透

1.购买隧道,获取authtoken和natapp客户端

https://natapp.cn/member/dashborad

2.cmd执行命令 ,启动natapp,cmd窗口查看域名地址

natapp -authtoken=[authtoken]

3.微信支付

1.官方下载微信sdk

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1

在这里插入图片描述

2.测试demo

1. MyWXPayConfig
package com.github.wxpay.sdk;

import java.io.InputStream;

public class MyWXPayConfig  extends WXPayConfig {

    /**
     * 获取 App ID
     *
     * @return App ID
     */
    String getAppID(){
        return "wx632c8f211f8122c6";
    }


    /**
     * 获取 Mch ID
     *
     * @return Mch ID
     */
    String getMchID(){
        return "1497984412";
    }


    /**
     * 获取 API 密钥
     *
     * @return API密钥
     */
    String getKey(){
        return "sbNCm1JnevqI36LrEaxFwcaT0hkGxFnC";
    }


    /**
     * 获取商户证书内容
     *
     * @return 商户证书内容
     */
    InputStream getCertStream(){
        return null;
    }

    /**
     * 获取WXPayDomain, 用于多域名容灾自动切换
     * @return
     */
    IWXPayDomain getWXPayDomain(){
        return new MyWXPayDomain();
    }

}

2.MyWXPayDomain
package com.zzhua.vadmin.wx;

public class MyWXPayDomain implements IWXPayDomain {

    @Override
    public void report(String domain, long elapsedTimeMillis, Exception ex) {

    }

    @Override
    public DomainInfo getDomain(WXPayConfig config) {
        DomainInfo domainInfo = new DomainInfo("api.mch.weixin.qq.com", true);
        return domainInfo;
    }
}
3.TestPay统一下单
public class TestPay {
    public static void main(String[] args) throws Exception {
        MyWXPayConfig payConfig = new MyWXPayConfig();
        WXPay wxPay = new WXPay(payConfig);

        Map<String,String> data = new HashMap<String,String>();
        data.put("body","PS酷教程");//商品标题
        data.put("out_trade_no","2020070410595900000016");//订单编号
        data.put("device_info","WEB");//设备信息
        data.put("fee_type","CNY");//单位:分
        data.put("total_fee","1");//支付金额
        data.put("spbill_create_ip","123.12.12.123");//终端ip ,记录ip,可以发现攻击我们的ip并进行屏蔽
        //重点:回调地址,用来通知支付结果的地址
        data.put("notify_url","http://au9btz.natappfree.cc/wx/notify"); // natapp提供的地址,由微信回调通知
        data.put("trade_type","NATIVE");
        data.put("product_id", "12");

        Map<String, String> order = wxPay.unifiedOrder(data); // 统一下单
        System.out.println(order);

    }
}
4.TestOrderQuery订单查询
public class TestOrderQuery {
    public static void main(String[] args) throws Exception {
        MyWXPayConfig payConfig = new MyWXPayConfig();
        WXPay wxPay = new WXPay(payConfig);

        HashMap<String, String> data = new HashMap<>();
        data.put("out_trade_no", "201907041059590016");
        Map<String, String> payResult = wxPay.orderQuery(data);
        System.out.println(payResult);

    }
}

3.将demo导入项目

1.WXPayController
@RequestMapping("wx")
@Controller
@Slf4j
public class WXPayController {
                                            // 内网穿透的地址 + 通知的url
    private final static String notifyUrl = "http://rkiscj.natappfree.cc/wx/notify"; 

    @RequestMapping("payOk")
    public String payOk(String orderId, Model model) {
        model.addAttribute("orderId",orderId);
        return "pay-ok";
    }

    /*第一步:生成orderId,带到前端,前端根据id请求支付二维码*/
    @RequestMapping("showPay")
    public String toPay(HttpServletRequest req) {
        String currTimeStr = DateUtil.format(new Date(), "yyyyMMddHHmmssSSS");
        String orderId = currTimeStr + "PSCOOL";
        req.setAttribute("orderId", orderId);
        return "show-pay";
    }

    /*第二步:根据前端传过来的id,请求微信接口获取code_url,将它生成二维码图片给img标签*/
    @RequestMapping("showQrcode")
    public void showPay(String orderId,HttpServletResponse resp) throws Exception {
        MyWXPayConfig payConfig = new MyWXPayConfig();
        WXPay wxPay = new WXPay(payConfig);

       if (StringUtils.isBlank(orderId)){
           throw new RuntimeException("出错...");
       }


        Map<String, String> data = new HashMap<String, String>();
        data.put("body", "PS酷教程");//商品标题
        data.put("out_trade_no", orderId);//订单编号
        data.put("device_info", "WEB");//设备信息
        data.put("fee_type", "CNY");//单位:分
        data.put("total_fee", "1");//支付金额
        data.put("spbill_create_ip", "123.12.12.123");//终端ip ,记录ip,可以发现攻击我们的ip并进行屏蔽
        //重点:回调地址,用来通知支付结果的地址
        data.put("notify_url", notifyUrl);
        data.put("trade_type", "NATIVE");
        data.put("product_id", "12");

        // 获取支付的code_url
        Map<String, String> order = wxPay.unifiedOrder(data);
        String code_url = order.get("code_url");

        // 生成支付的二维码
        BufferedImage bufferedImage = ZXingCodeEncodeUtil.generateQrcode(code_url, "PNG");
        ImageIO.write(bufferedImage, "PNG", resp.getOutputStream());
        log.info("生成支付二维码: {}", code_url);

    }

    /*第三步:用户支付成功后,微信会调用此接口,传回信息*/
    @RequestMapping("notify")
    public void doNotify(HttpServletRequest req, HttpServletResponse resp) throws Exception {

        log.info("回调接口...");

        ServletInputStream is = req.getInputStream();


        byte[] bytes = new byte[1024];

        int len;
        StringBuffer buffer = new StringBuffer();
        while ((len = is.read(bytes)) != -1) {
            String s = new String(bytes, 0, len);
            buffer.append(s);
        }

        // 给微信一个标准的应答
        resp.getWriter().write("<xml>\n" +
                "  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
                "  <return_msg><![CDATA[OK]]></return_msg>\n" +
                "</xml>");


        // 解析微信返回的xml消息
        Map<String, String> map = WXPayUtil.xmlToMap(buffer.toString());
        String orderId = map.get("out_trade_no");


        /*第四步:使用goEasy通知前端支付成功后跳转*/
        // https://www.goeasy.io/cn/get-start.html [goeasy官网]
        GoEasy goEasy = new GoEasy("http://rest-hangzhou.goeasy.io",
                                   "BC-deec2d7dc9e0461bb4b0c51e0b4bcc88");
        goEasy.publish(orderId, "ojbk");

    }

}
2.show-pay.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>支付页面</title>
</head>
<body>
    <h1 style="color: red;">支付</h1>
    <img th:src="@{'/wx/showQrcode?orderId='+${orderId}}" />
</body>

<!--[if lte IE 8]>//如果需要支持低版本的IE8及以下
<script type="text/javascript" src="https://cdn.goeasy.io/json2.js"></script>
<![endif]-->
<script type="text/javascript" src="https://cdn.goeasy.io/goeasy-1.0.11.js"></script>
<script type="text/javascript">
    var goEasy = new GoEasy({
        host: 'hangzhou.goeasy.io',//应用所在的区域地址,杭州:hangzhou.goeasy.io,新加坡:singapore.goeasy.io
        appkey: "BC-deec2d7dc9e0461bb4b0c51e0b4bcc88",//替换为您的应用appkey
        forceTLS: false, //如果需要使用HTTPS/WSS,请设置为true,默认为false
        onConnected: function () {
            console.log('连接成功!')
        },
        onDisconnected: function () {
            console.log('连接断开!')
        },
        onConnectFailed: function (error) {
            console.log('连接失败或错误!')
        }
    });

    var orderId = '[[${orderId}]]';

    goEasy.subscribe({
        channel: orderId,//替换为您自己的channel
        onMessage: function (message) {
            console.log("Channel:" + message.channel + " content:" + message.content);

            location.href = '/wx/payOk?orderId='+orderId;
        }
    });

</script>


</html>
3.pay-ok.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 style="color: red;"><span th:text="${orderId}"></span>支付成功</h1>
</body>
</html>
4.项目依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zzhua</groupId>
    <artifactId>v-admin</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>v-admin</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <zxing.version>3.4.0</zxing.version>
        <project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.2.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>${zxing.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>${zxing.version}</version>
        </dependency>

        <!--微信支付相关开始-->
        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.3</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.21</version>
        </dependency>
        <!--微信支付相关结束-->

        <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.5</version>
        </dependency>

        <dependency>
            <groupId>io.goeasy</groupId>
            <artifactId>goeasy-sdk</artifactId>
            <version>0.3.14</version>
        </dependency>


    </dependencies>

    <licenses>
        <license>
            <name>The BSD 3-Clause License</name>
            <url>https://opensource.org/licenses/BSD-3-Clause</url>
            <distribution>repo</distribution>
        </license>
    </licenses>

    <scm>
        <url></url>
        <connection></connection>
        <developerConnection></developerConnection>
    </scm>

    <developers>
        <developer>
            <name>wxpay</name>
            <email></email>
            <url>https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=11_1</url>
        </developer>
    </developers>

    <profiles>
        <profile>
            <id>release</id>
            <distributionManagement>
                <snapshotRepository>
                    <id>oss</id>
                    <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
                </snapshotRepository>
                <repository>
                    <id>oss</id>
                    <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
                </repository>
            </distributionManagement>
            <build>
                <plugins>
                    <!-- Source -->
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-source-plugin</artifactId>
                        <version>3.0.1</version>
                        <executions>
                            <execution>
                                <phase>package</phase>
                                <goals>
                                    <goal>jar-no-fork</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <!-- Javadoc -->
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-javadoc-plugin</artifactId>
                        <version>2.10.4</version>
                        <executions>
                            <execution>
                                <phase>package</phase>
                                <goals>
                                    <goal>jar</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <!-- Gpg Signature -->
                    <!--<plugin>-->
                        <!--<groupId>org.apache.maven.plugins</groupId>-->
                        <!--<artifactId>maven-gpg-plugin</artifactId>-->
                        <!--<version>1.6</version>-->
                        <!--<executions>-->
                            <!--<execution>-->
                                <!--<id>sign-artifacts</id>-->
                                <!--<phase>verify</phase>-->
                                <!--<goals>-->
                                    <!--<goal>sign</goal>-->
                                <!--</goals>-->
                            <!--</execution>-->
                        <!--</executions>-->
                    <!--</plugin>-->
                </plugins>
            </build>
        </profile>
    </profiles>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

4.演示:

1.访问支付页面

在这里插入图片描述

2.支付成功后,浏览器自动跳转

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值