注意:本文appid、mch_id、key都是属于来自传智播客的,本文只用来学习,希望读者不要用于商业用途。
1、去微信支付开发文档里面取下载好属于java的demo https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1
如图:
下载好了以后利用开发工具打开,每个类都有对应的代码注释,笔者在这里就不对他们一一讲解了。
2、做一个微信统一下单的模拟,可以通过它的开发文档知道哪些是必要的参数
微信统一下单地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
3、直接贴代码
appid、mch_id、key都是属于来自传智播客的,希望读者不要用于商业用途,只用来学习。
package com.github.wxpay.test;
import com.github.wxpay.sdk.IWXPayDomain;
import com.github.wxpay.sdk.WXPayConfig;
import java.io.InputStream;
public class Config extends WXPayConfig {
/**
* appId
* */
private static final String appId="wx8397f8696b538317";
/**
* mch_id
* */
private static final String mchId = "1473426802";
/**
* key
* */
private static final String key = "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb";
@Override
public String getAppID() {
return appId;
}
@Override
public String getMchID() {
return mchId;
}
@Override
public String getKey() {
return key;
}
/**
* 证书,暂时不需要
* @return
*/
@Override
public InputStream getCertStream() {
return null;
}
/**
* 域名
* @return
*/
@Override
public IWXPayDomain getWXPayDomain() {
return new IWXPayDomain() {
public void report(String domain, long elapsedTimeMillis, Exception ex) {
}
public DomainInfo getDomain(WXPayConfig config) {
return new DomainInfo("api.mch.weixin.qq.com",true);
}
};
}
}
4、进行测试
package com.github.wxpay.test;
import com.github.wxpay.sdk.WXPayRequest;
import com.github.wxpay.sdk.WXPayUtil;
import java.util.HashMap;
import java.util.Map;
public class WXPayTest {
public static void main(String[] args) throws Exception {
Config config = new Config();
//1、封装参数
Map<String, String> map = new HashMap<String, String>();
map.put("appid", config.getAppID());//公账号id
map.put("mch_id", config.getMchID());//商户号
map.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串
map.put("body", "商品描述--测试");//商品描述
map.put("out_trade_no", "22222122112");//商户订单号
map.put("total_fee", "1");//金额
map.put("spbill_create_ip", "127.0.0.1");//终端ip
map.put("notify_url", "http://www.baidu.com");//通知地址
map.put("trade_type", "NATIVE");//交易类型 native本地支付
//微信提供的工具包
String xmlParam = WXPayUtil.generateSignedXml(map, config.getKey());
System.out.println("参数内容:" );
System.out.println(xmlParam);
//2、发送请求
WXPayRequest wxPayRequest = new WXPayRequest(config);
String xmlResult = wxPayRequest.requestWithCert("/pay/unifiedorder", null, xmlParam, false);
System.out.println("参数结果:");
System.out.println(xmlResult);
//解析返回结果
Map<String ,String> m = WXPayUtil.xmlToMap(xmlResult);
String url = m.get("code_url");
System.out.println("支付地址:"+url);
}
}
过程:
1)封装参数:将统一下单里面的必要参数封装成xml文件
2)发送请求:将封装好的xml提交给微信支付系统
3)解析返回结果:如果xml没有问题,微信支付系统就会给我们一个url,这个URL是支付地址
如图:
5、最后一步,将支付地址放入到二维码里面就可以了,如果生成二维码有困难,可以查看这篇文章:https://blog.csdn.net/qq_36138652/article/details/114986021
记得一定要修改二维码里面的内容,因为二维码里面的内容就是你支付的url
package com.code;
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;
import org.apache.commons.lang3.StringUtils;
import javax.imageio.ImageIO;
import javax.swing.filechooser.FileSystemView;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class QRCodeUtil {
/**
* CODE_WIDTH:二维码宽度,单位像素
* CODE_HEIGHT:二维码高度,单位像素
* FRONT_COLOR:二维码前景色,0x000000 表示黑色
* BACKGROUND_COLOR:二维码背景色,0xFFFFFF 表示白色
* 演示用 16 进制表示,和前端页面 CSS 的取色是一样的,注意前后景颜色应该对比明显,如常见的黑白
*/
private static final int CODE_WIDTH = 400;
private static final int CODE_HEIGHT = 400;
private static final int FRONT_COLOR = 0x000000;
private static final int BACKGROUND_COLOR = 0xFFFFFF;
public static void main(String[] args) {
String codeContent1 = "weixin://wxpay/bizpayurl?pr=JJ4LTGRzz";
File file = new File("D:/codes");
String fileName = "z"+System.currentTimeMillis()+".png";
createCodeToFile(codeContent1, file, fileName,"");
}
/**
* 生成二维码 并 保存为图片
*/
/**
* @param codeContent :二维码参数内容,如果是一个网页地址,如 https://www.baidu.com/ 则 微信扫一扫会直接进入此地址
* 如果是一些参数,如 1541656080837,则微信扫一扫会直接回显这些参数值
* @param codeImgFileSaveDir :二维码图片保存的目录,如 D:/codes
* @param fileName :二维码图片文件名称,带格式,如 123.png
*/
public static void createCodeToFile(String codeContent, File codeImgFileSaveDir, String fileName,String note) {
try {
/** 参数检验*/
if (codeContent == null || "".equals(codeContent)) {
System.out.println("二维码内容为空,不进行操作...");
return;
}
codeContent = codeContent.trim();
if (codeImgFileSaveDir == null || codeImgFileSaveDir.isFile()) {
codeImgFileSaveDir = FileSystemView.getFileSystemView().getHomeDirectory();
System.out.println("二维码图片存在目录为空,默认放在桌面...");
}
if (!codeImgFileSaveDir.exists()) {
codeImgFileSaveDir.mkdirs();
System.out.println("二维码图片存在目录不存在,开始创建...");
}
if (fileName == null || "".equals(fileName)) {
fileName = new Date().getTime() + ".png";
System.out.println("二维码图片文件名为空,随机生成 png 格式图片...");
}
/**com.google.zxing.EncodeHintType:编码提示类型,枚举类型
* EncodeHintType.CHARACTER_SET:设置字符编码类型
* EncodeHintType.ERROR_CORRECTION:设置误差校正
* ErrorCorrectionLevel:误差校正等级,L = ~7% correction、M = ~15% correction、Q = ~25% correction、H = ~30% correction
* 不设置时,默认为 L 等级,等级不一样,生成的图案不同,但扫描的结果是一样的
* EncodeHintType.MARGIN:设置二维码边距,单位像素,值越小,二维码距离四周越近
* */
Map<EncodeHintType, Object> hints = new HashMap();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
hints.put(EncodeHintType.MARGIN, 1);
/**
* MultiFormatWriter:多格式写入,这是一个工厂类,里面重载了两个 encode 方法,用于写入条形码或二维码
* encode(String contents,BarcodeFormat format,int width, int height,Map<EncodeHintType,?> hints)
* contents:条形码/二维码内容
* format:编码类型,如 条形码,二维码 等
* width:码的宽度
* height:码的高度
* hints:码内容的编码类型
* BarcodeFormat:枚举该程序包已知的条形码格式,即创建何种码,如 1 维的条形码,2 维的二维码 等
* BitMatrix:位(比特)矩阵或叫2D矩阵,也就是需要的二维码
*/
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
BitMatrix bitMatrix = multiFormatWriter.encode(codeContent, BarcodeFormat.QR_CODE, CODE_WIDTH, CODE_HEIGHT, hints);
/**java.awt.image.BufferedImage:具有图像数据的可访问缓冲图像,实现了 RenderedImage 接口
* BitMatrix 的 get(int x, int y) 获取比特矩阵内容,指定位置有值,则返回true,将其设置为前景色,否则设置为背景色
* BufferedImage 的 setRGB(int x, int y, int rgb) 方法设置图像像素
* x:像素位置的横坐标,即列
* y:像素位置的纵坐标,即行
* rgb:像素的值,采用 16 进制,如 0xFFFFFF 白色
*/
BufferedImage bufferedImage = new BufferedImage(CODE_WIDTH, CODE_HEIGHT, BufferedImage.TYPE_INT_BGR);
for (int x = 0; x < CODE_WIDTH; x++) {
for (int y = 0; y < CODE_HEIGHT; y++) {
bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? FRONT_COLOR : BACKGROUND_COLOR);
}
}
/**javax.imageio.ImageIO java 扩展的图像IO
* write(RenderedImage im,String formatName,File output)
* im:待写入的图像
* formatName:图像写入的格式
* output:写入的图像文件,文件不存在时会自动创建
*
* 即将保存的二维码图片文件*/
File codeImgFile = new File(codeImgFileSaveDir, fileName);
ImageIO.write(bufferedImage, "png", codeImgFile);
System.out.println("二维码图片生成成功:" + codeImgFile.getPath());
// 自定义文本描述
if (StringUtils.isNotEmpty(note)) {
// 新的图片,把带logo的二维码下面加上文字
BufferedImage outImage = new BufferedImage(400, 445, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D outg = outImage.createGraphics();
// 画二维码到新的面板
outg.drawImage(bufferedImage, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), null);
// 画文字到新的面板
outg.setColor(Color.BLACK);
outg.setFont(new Font("楷体", Font.BOLD, 30)); // 字体、字型、字号
int strWidth = outg.getFontMetrics().stringWidth(note);
Integer height = 400;
if (strWidth > 399) {
//长度过长就截取前面部分
// 长度过长就换行
String note1 = note.substring(0, note.length() / 2);
String note2 = note.substring(note.length() / 2, note.length());
int strWidth1 = outg.getFontMetrics().stringWidth(note1);
int strWidth2 = outg.getFontMetrics().stringWidth(note2);
outg.drawString(note1, 200 - strWidth1 / 2, height + (outImage.getHeight() - height) / 2 + 12);
BufferedImage outImage2 = new BufferedImage(400, 485, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D outg2 = outImage2.createGraphics();
outg2.drawImage(outImage, 0, 0, outImage.getWidth(), outImage.getHeight(), null);
outg2.setColor(Color.BLACK);
outg2.setFont(new Font("宋体", Font.BOLD, 30)); // 字体、字型、字号
outg2.drawString(note2, 200 - strWidth2 / 2, outImage.getHeight() + (outImage2.getHeight() - outImage.getHeight()) / 2 + 5);
outg2.dispose();
outImage2.flush();
outImage = outImage2;
} else {
outg.drawString(note, 200 - strWidth / 2, height + (outImage.getHeight() - height) / 2 + 12); // 画文字
}
outg.dispose();
outImage.flush();
bufferedImage = outImage;
}
bufferedImage.flush();
/**
* 区别就是以一句,输出到输出流中,如果第三个参数是 File,则输出到文件中
*/
ImageIO.write(bufferedImage, "png",codeImgFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}