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.支付成功后,浏览器自动跳转