1、二维码的生成
二维码生成坐标
<!-- zxing生成二维码 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
二维码生成工具类①
package com.xuechengpluscommon.utils;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.apache.commons.lang3.StringUtils;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
/**
* @author Mr.M
* @version 1.0
* @description 二维码生成工具
* @date 2022/10/3 0:03
*/
public class QRCodeUtil {
/**
* 生成二维码
*
* @param content 二维码对应的URL
* @param width 二维码图片宽度
* @param height 二维码图片高度
* @return
*/
public String createQRCode(String content, int width, int height) throws IOException {
String resultImage = "";
//除了尺寸,传入内容不能为空
if (!StringUtils.isEmpty(content)) {
ServletOutputStream stream = null;
ByteArrayOutputStream os = new ByteArrayOutputStream();
//二维码参数
@SuppressWarnings("rawtypes")
HashMap<EncodeHintType, Comparable> hints = new HashMap<>();
//指定字符编码为“utf-8”
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//L M Q H四个纠错等级从低到高,指定二维码的纠错等级为M
//纠错级别越高,可以修正的错误就越多,需要的纠错码的数量也变多,相应的二维吗可储存的数据就会减少
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
//设置图片的边距
hints.put(EncodeHintType.MARGIN, 1);
try {
//zxing生成二维码核心类
QRCodeWriter writer = new QRCodeWriter();
//把输入文本按照指定规则转成二维吗
BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
//生成二维码图片流
BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
//输出流
ImageIO.write(bufferedImage, "png", os);
/**
* 原生转码前面没有 data:image/png;base64 这些字段,返回给前端是无法被解析,所以加上前缀
*/
resultImage = new String("data:image/png;base64," + EncryptUtil.encodeBase64(os.toByteArray()));
return resultImage;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("生成二维码出错");
} finally {
if (stream != null) {
stream.flush();
stream.close();
}
}
}
return null;
}
public static void main(String[] args) throws IOException {
QRCodeUtil qrCodeUtil = new QRCodeUtil();
System.out.println(qrCodeUtil.createQRCode("http://192.168.101.1:63030/orders/alipaytest", 200, 200));
}
}
二维码生成工具类②
package com.xuechengpluscommon.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Base64;
public class EncryptUtil {
private static final Logger logger = LoggerFactory.getLogger(EncryptUtil.class);
public static String encodeBase64(byte[] bytes){
String encoded = Base64.getEncoder().encodeToString(bytes);
return encoded;
}
public static byte[] decodeBase64(String str){
byte[] bytes = null;
bytes = Base64.getDecoder().decode(str);
return bytes;
}
public static String encodeUTF8StringBase64(String str){
String encoded = null;
try {
encoded = Base64.getEncoder().encodeToString(str.getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
logger.warn("不支持的编码格式",e);
}
return encoded;
}
public static String decodeUTF8StringBase64(String str){
String decoded = null;
byte[] bytes = Base64.getDecoder().decode(str);
try {
decoded = new String(bytes,"utf-8");
}catch(UnsupportedEncodingException e){
logger.warn("不支持的编码格式",e);
}
return decoded;
}
public static String encodeURL(String url) {
String encoded = null;
try {
encoded = URLEncoder.encode(url, "utf-8");
} catch (UnsupportedEncodingException e) {
logger.warn("URLEncode失败", e);
}
return encoded;
}
public static String decodeURL(String url) {
String decoded = null;
try {
decoded = URLDecoder.decode(url, "utf-8");
} catch (UnsupportedEncodingException e) {
logger.warn("URLDecode失败", e);
}
return decoded;
}
public static void main(String [] args){
String str = "abcd{'a':'b'}";
String encoded = EncryptUtil.encodeUTF8StringBase64(str);
String decoded = EncryptUtil.decodeUTF8StringBase64(encoded);
System.out.println(str);
System.out.println(encoded);
System.out.println(decoded);
String url = "== wo";
String urlEncoded = EncryptUtil.encodeURL(url);
String urlDecoded = EncryptUtil.decodeURL(urlEncoded);
System.out.println(url);
System.out.println(urlEncoded);
System.out.println(urlDecoded);
}
}
测试生成二维码
@Test
void contextLoads() {
try {
String helloWorld = new QRCodeUtil().createQRCode("http://d6nt2p.natappfree.cc/pay/test", 200, 200);
System.out.println(helloWorld);
} catch (IOException e) {
e.printStackTrace();
}
}
结果(二维码内容如果是网址则会跳转,如果是接口,则返回接口数据)
2、订单号生成工具类
package com.xuechengpluscommon.utils;
import java.util.Random;
/**
* snow flow .
*
*/
public final class IdWorkerUtils {
private static final Random RANDOM = new Random();
private static final long WORKER_ID_BITS = 5L;
private static final long DATACENTERIDBITS = 5L;
private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTERIDBITS);
private static final long SEQUENCE_BITS = 12L;
private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTERIDBITS;
private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
private static final IdWorkerUtils ID_WORKER_UTILS = new IdWorkerUtils();
private long workerId;
private long datacenterId;
private long idepoch;
private long sequence = '0';
private long lastTimestamp = -1L;
private IdWorkerUtils() {
this(RANDOM.nextInt((int) MAX_WORKER_ID), RANDOM.nextInt((int) MAX_DATACENTER_ID), 1288834974657L);
}
private IdWorkerUtils(final long workerId, final long datacenterId, final long idepoch) {
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_WORKER_ID));
}
if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", MAX_DATACENTER_ID));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
this.idepoch = idepoch;
}
/**
* Gets instance.
*
* @return the instance
*/
public static IdWorkerUtils getInstance() {
return ID_WORKER_UTILS;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & SEQUENCE_MASK;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - idepoch) << TIMESTAMP_LEFT_SHIFT)
| (datacenterId << DATACENTER_ID_SHIFT)
| (workerId << WORKER_ID_SHIFT) | sequence;
}
private long tilNextMillis(final long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
/**
* Build part number string.
*
* @return the string
*/
public String buildPartNumber() {
return String.valueOf(ID_WORKER_UTILS.nextId());
}
/**
* Create uuid string.
*
* @return the string
*/
public String createUUID() {
return String.valueOf(ID_WORKER_UTILS.nextId());
}
public static void main(String[] args) {
System.out.println(IdWorkerUtils.getInstance().nextId());
}
}
3、导入阿里支付坐标
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.34.0.ALL</version>
</dependency>
这里需要三个重要的数据:(沙箱模式或者已是商户才可以拥有)
APPID(appid)
RSA_PRIVATE_KEY(应用密匙)
ALIPAY_PUBLIC_KEY(应用公匙)
4、进行订单支付(被动获取订单支付状态)
详细地址:电脑网站支付快速接入 - 支付宝文档中心 (alipay.com)
@RequestMapping("/aliyuntest/pc")
public void aliyuntest (HttpServletRequest httpRequest,
HttpServletResponse httpResponse) throws ServletException, IOException {
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE); //获得初始化的AlipayClient
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); //创建API对应的request
// alipayRequest.setReturnUrl( "http://192.168.31.196:63100/pay/payNotify" );
alipayRequest.setNotifyUrl( "http://d6nt2p.natappfree.cc/pay/payNotify" ); //在公共参数中设置回跳和通知地址
alipayRequest.setBizContent( "{" +
" \"out_trade_no\":\"2012523432526230\"," +
" \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +
" \"total_amount\":5688.88," +
" \"subject\":\"Iphone14pro 8+256G\"," +
" \"body\":\"Iphone14pro 8+256G\"," +
" \"passback_params\":\"merchantBizType%3d3C%26merchantBizNo%2012523432526230\"," +
" \"extend_params\":{" +
" \"sys_service_provider_id\":\"201413352010\"" +
" }" +
" }" ); //填充业务参数
String form= "" ;
try {
form = alipayClient.pageExecute(alipayRequest).getBody(); //调用SDK生成表单
} catch (AlipayApiException e) {
e.printStackTrace();
}
httpResponse.setContentType( "text/html;charset=" + AlipayConfig.CHARSET);
httpResponse.getWriter().write(form); //直接将完整的表单html输出到页面
httpResponse.getWriter().flush();
httpResponse.getWriter().close();
}
alipayRequest.setBizContent()的内容可以自己进行替换
alipayRequest.setNotifyUrl()里面的地址必须公网也可以访问,即意味着无法使用本地服务器路径,这里使用内网穿透
详细地址:(4条消息) 使用natapp实现内网穿透详细教程_小龙Hibernation的博客-CSDN博客
接口测试:
5、主动获取订单支付状态
@GetMapping("/queryPayResult")
@ResponseBody
public String queryPayResult() throws AlipayApiException {
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL,AlipayConfig.APPID,AlipayConfig.RSA_PRIVATE_KEY,AlipayConfig.FORMAT,AlipayConfig.CHARSET,AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", "2012523432526225");
//bizContent.put("trade_no", "2023021722001443820502204183");
request.setBizContent(bizContent.toString());
AlipayTradeQueryResponse response = alipayClient.execute(request);
System.out.println("____________________________________");
String body = response.getBody();
System.out.println("____________________________________");
if(response.isSuccess()){
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
return body;
}
将商品Id输入之后便可查询结果
6、被动获取订单支付结果
@RequestMapping("/payNotify")
public void payNotify(HttpServletRequest request,HttpServletResponse response) throws IOException, AlipayApiException {
Map<String,String> params = new HashMap<String,String>();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
params.put(name, valueStr);
}
//获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
//商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
//支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
//交易状态
String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");
//获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
//计算得出通知验证结果
//boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
boolean verify_result = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, "RSA2");
if(verify_result){//验证成功
//
//请在这里加上商户的业务逻辑程序代码
//——请根据您的业务逻辑来编写程序(以下代码仅作参考)——
if(trade_status.equals("TRADE_FINISHED")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
//如果有做过处理,不执行商户的业务程序
//注意:
//如果签约的是可退款协议,退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
//如果没有签约可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
} else if (trade_status.equals("TRADE_SUCCESS")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
//如果有做过处理,不执行商户的业务程序
log.info("=========================支付成功=========================");
//注意:
//如果签约的是可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
}
//——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
response.getWriter().println("success");
//请不要修改或删除
//
}else{//验证失败
response.getWriter().println("fail");
}