ソースを参照

feature: 微信收款二维码生成

luo.yibo@datuai.com 1 年間 前
コミット
b6801133b0
15 ファイル変更634 行追加36 行削除
  1. 28 2
      pom.xml
  2. 17 1
      ruoyi-modules/ruoyi-backstage/pom.xml
  3. 48 6
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/business/payments/ThirdPayBusiness.java
  4. 3 3
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/business/self/SelfBusiness.java
  5. 0 19
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/config/WechatPayConfig.java
  6. 1 2
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/controller/self/TeacherController.java
  7. 84 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/config/MerchantPayServiceConfigurer.java
  8. 33 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/config/ThirdPayBaseConfig.java
  9. 39 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/config/WechatPayConfig.java
  10. 1 2
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/controller/ThirdPayController.java
  11. 75 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/handlers/WxPayMessageHandler.java
  12. 87 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/util/BufferedImageLuminanceSource.java
  13. 70 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/util/Img2Base64Util.java
  14. 147 0
      ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/util/QRCodeUtil.java
  15. 1 1
      ruoyi-modules/ruoyi-backstage/src/main/resources/MET-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

+ 28 - 2
pom.xml

@@ -62,7 +62,10 @@
         <flowable.version>7.0.0</flowable.version>
         <!-- mq配置 -->
         <rocketmq.version>2.3.0</rocketmq.version>
-
+        <!-- 二维码工具 -->
+        <qrcode.version>3.5.3</qrcode.version>
+        <!-- 聚合支付 -->
+        <egzosn.version>3.5.3</egzosn.version>
         <!-- 插件版本 -->
         <maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
         <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
@@ -423,7 +426,30 @@
                 <artifactId>commons-codec</artifactId>
                 <version>${commons-codec.version}</version>
             </dependency>
-
+<!--            二维码工具-->
+            <dependency>
+                <groupId>com.google.zxing</groupId>
+                <artifactId>core</artifactId>
+                <version>${qrcode.version}</version>
+            </dependency>
+<!--            聚合支付-->
+            <dependency>
+                <groupId>com.egzosn</groupId>
+                <artifactId>pay-spring-boot-starter</artifactId>
+                <version>0.0.4</version>
+            </dependency>
+            <!-- 支付宝 -->
+            <dependency>
+                <groupId>com.egzosn</groupId>
+                <artifactId>pay-java-ali</artifactId>
+                <version>2.13.1</version>
+            </dependency>
+            <!-- 微信 -->
+            <dependency>
+                <groupId>com.egzosn</groupId>
+                <artifactId>pay-java-wx</artifactId>
+                <version>2.13.1</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 

+ 17 - 1
ruoyi-modules/ruoyi-backstage/pom.xml

@@ -143,7 +143,23 @@
             <version>2.2.0</version>
             <scope>compile</scope>
         </dependency>
-
+        <dependency>
+            <groupId>com.egzosn</groupId>
+            <artifactId>pay-spring-boot-starter</artifactId>
+            <version>0.0.4</version>
+        </dependency>
+        <!-- 支付宝 -->
+        <dependency>
+            <groupId>com.egzosn</groupId>
+            <artifactId>pay-java-ali</artifactId>
+            <version>2.13.1</version>
+        </dependency>
+        <!-- 微信 -->
+        <dependency>
+            <groupId>com.egzosn</groupId>
+            <artifactId>pay-java-wx</artifactId>
+            <version>2.13.1</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 48 - 6
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/business/payments/ThirdPayBusiness.java

@@ -3,19 +3,29 @@ package org.dromara.backstage.business.payments;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.egzosn.pay.common.util.DateUtils;
+import com.egzosn.pay.spring.boot.core.PayServiceManager;
+import com.egzosn.pay.spring.boot.core.bean.MerchantPayOrder;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.dromara.backstage.payment.config.WechatPayConfig;
 import org.dromara.backstage.payment.domain.bo.PayOrderBo;
 import org.dromara.backstage.payment.domain.bo.PurseInOutBo;
 import org.dromara.backstage.payment.domain.vo.PayOrderVo;
 import org.dromara.backstage.payment.service.IPayOrderService;
+import org.dromara.backstage.payment.util.Img2Base64Util;
+import org.dromara.backstage.payment.util.QRCodeUtil;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.enums.BalanceUpdateEnum;
 import org.dromara.common.core.enums.CreditStatusEnum;
 import org.dromara.common.core.enums.CreditTypeEnum;
+import org.dromara.common.core.utils.SpringUtils;
 import org.springframework.stereotype.Service;
 
+import java.math.BigDecimal;
 import java.text.MessageFormat;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -35,22 +45,23 @@ import java.util.Map;
 public class ThirdPayBusiness {
     private final IPayOrderService payOrderService;
     private final PayOrderBusiness payOrderBusiness;
+    private final WechatPayConfig wechatPayConfig;
 
     /**
      * 生成充值收款二维码
      * @param bo 充值订单
      * @return 二维码信息
      */
-    public R<Map<String,String>> createCollectQrCode(PayOrderBo bo){
+    public R<Map<String,String>> createCollectQrCode(PayOrderBo bo) throws Exception {
         R<PayOrderVo> result = createThirdPayOrder(bo);
         if (R.isError(result)) {
             return R.fail(result.getMsg());
         }
-        Map<String, String> mapCode = new HashMap<>();
-        // TODO 2025-01-13 19:36:29 luoyibo 生成微信收款码
-        mapCode.put("qrCode", "");
-        mapCode.put("orderSn", result.getData().getOrderId().toString());
-        return R.ok(mapCode);
+        String orderSn = result.getData().getOrderId().toString();
+        String callBackUrl = result.getData().getBackUrl() + orderSn;
+        bo.setBackUrl(callBackUrl);
+
+        return createWechatQrCode(result.getData());
     }
 
     /**
@@ -169,4 +180,35 @@ public class ThirdPayBusiness {
         }
         return R.ok(vo);
     }
+
+    public R<Map<String,String>> createWechatQrCode(PayOrderVo vo) throws Exception {
+        String orderSn = vo.getOrderId().toString();
+        String callBackUrl = vo.getBackUrl() + orderSn;
+        BigDecimal amount = vo.getReceiptMoney();
+        Map<String, String> mapCode = new HashMap<>();
+        mapCode.put("orderSn", vo.getOrderId().toString());
+
+        String detailsId = "2";
+        String wayTrade = "NATIVE";
+        Map<String, Object> addition = new HashMap<String, Object>();
+        addition.put("callBackUrl", callBackUrl);
+
+        MerchantPayOrder payOrder = new MerchantPayOrder(detailsId, wayTrade, vo.getTitle(), "摘要",
+                                                         vo.getReceiptMoney(), orderSn);
+        payOrder.setAddition(JSONObject.toJSONString(addition));
+
+        PayServiceManager manager = SpringUtils.getBean(PayServiceManager.class);
+        // 存放在二维码中的内容
+        String text = manager.getQrPay(payOrder);
+        // String text = "";
+        // 嵌入二维码的图片路径
+        String imgPath = wechatPayConfig.getQrCodeEmbed();
+        // 生成的二维码的路径及名称
+        String destPath = wechatPayConfig.getQrCodeDir() + "/"+ DateUtils.formatDate(new Date(), "yyyy/MM/dd/HHmmssSSS-") + amount + ".jpg";
+        // 生成二维码
+        QRCodeUtil.encode(text, imgPath, destPath, true);
+        mapCode.put("qrCode", Img2Base64Util.getImgStr(destPath));
+
+        return R.ok(mapCode);
+    }
 }

+ 3 - 3
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/business/self/SelfBusiness.java

@@ -122,7 +122,7 @@ public class SelfBusiness {
     public void sendSmsNoRoom(String phone,String realName,String className){
         sendSms(phone, String.format(messageNoRoom.replace("rn", "\r\n"), realName, className));
     }
-    public R<Map<String, String>> getWechatRechargeQrCode(Map<String, String> mapParams){
+    public R<Map<String, String>> getWechatRechargeQrCode(Map<String, String> mapParams) throws Exception {
         int workStationNumb = Integer.parseInt(mapParams.get("workStationNumb"));
         String userId = mapParams.get("userId");
         String userXm = mapParams.get("userXm");
@@ -131,7 +131,7 @@ public class SelfBusiness {
         String mobile = mapParams.get("mobile");
         BigDecimal dealValue = new BigDecimal(mapParams.get("dealValue"));
 
-        String callBackUrl = rechargeBackUrl + "/teacher/recharge/" + userId + "/" + dealValue + "/" + workStationNumb + "/";
+        String callBackUrl = rechargeBackUrl + "/result/";
         PayOrderBo payOrderBo = new PayOrderBo();
         payOrderBo.setCreditTime(DateUtil.date());
         payOrderBo.setCreditType(CreditTypeEnum.WECHAT_RECHARGE.code().toString());
@@ -139,7 +139,7 @@ public class SelfBusiness {
         payOrderBo.setPayStatus(PayStatusEnum.UNPAID.code().toString());
         payOrderBo.setReceiptMoney(dealValue);
         payOrderBo.setCreditStatus(CreditStatusEnum.CREATE.code().toString());
-        payOrderBo.setTitle("");
+        payOrderBo.setTitle(CreditTypeEnum.WECHAT_RECHARGE.message());
         payOrderBo.setBackUrl(callBackUrl);
         payOrderBo.setRealName(userXm);
         payOrderBo.setUserId(Long.parseLong(userId));

+ 0 - 19
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/config/WechatPayConfig.java

@@ -1,19 +0,0 @@
-package org.dromara.backstage.config;
-
-import lombok.Data;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * name: WechatPayConfig
- * package: org.dromara.backstage.config
- * description: 微信支付配置
- * date: 2025-01-13 19:19:53 19:19
- *
- * @author luoyibo
- * @version 0.1
- * @since JDK 1.8
- */
-@Configuration
-@Data
-public class WechatPayConfig {
-}

+ 1 - 2
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/controller/self/TeacherController.java

@@ -19,7 +19,6 @@ import org.springframework.web.bind.annotation.*;
 
 import java.math.BigDecimal;
 import java.text.MessageFormat;
-import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -87,7 +86,7 @@ public class TeacherController {
      * @return 二维码
      */
     @PostMapping("/api/v1/wechat/recharge/qrcode")
-    public ReturnResult getWeChatRechargeCode(@RequestBody Map<String, String> mapParams) {
+    public ReturnResult getWeChatRechargeCode(@RequestBody Map<String, String> mapParams) throws Exception {
         R<Map<String,String>> result = selfBusiness.getWechatRechargeQrCode(mapParams);
         if (R.isSuccess(result)) {
             return ReturnResult.success(result.getData());

+ 84 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/config/MerchantPayServiceConfigurer.java

@@ -0,0 +1,84 @@
+package org.dromara.backstage.payment.config;
+
+import com.egzosn.pay.common.http.HttpConfigStorage;
+import com.egzosn.pay.spring.boot.core.PayServiceConfigurer;
+import com.egzosn.pay.spring.boot.core.configurers.MerchantDetailsServiceConfigurer;
+import com.egzosn.pay.spring.boot.core.configurers.PayMessageConfigurer;
+import lombok.RequiredArgsConstructor;
+import org.dromara.backstage.payment.handlers.WxPayMessageHandler;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @ClassName MerchantPayServiceConfigurer
+ * @Description 支付服务配置
+ * @Author luoyibo
+ * @Date 2025-01-14 21:00
+ * @Version 1.0
+ * @since jdk17
+ */
+@Configuration
+@RequiredArgsConstructor
+public class MerchantPayServiceConfigurer implements PayServiceConfigurer {
+
+    private final WxPayMessageHandler wxPayMessageHandler;
+    private final WechatPayConfig wechatPayConfig;
+    @Override
+    public void configure(MerchantDetailsServiceConfigurer merchants) {
+        //        数据库文件存放 /doc/sql目录下
+        //        merchants.jdbc().template(jdbcTemplate);
+        //微信请求配置,详情参考https://gitee.com/egzosn/pay-java-parent项目中的使用
+        HttpConfigStorage wxHttpConfigStorage = new HttpConfigStorage();
+        //        wxHttpConfigStorage.setKeystore("http://www.egzosn.com/certs/ssl 退款证书");
+        //        wxHttpConfigStorage.setCertStoreType(CertStoreType.URL);
+        //        wxHttpConfigStorage.setStorePassword("ssl 证书对应的密码, 默认为商户号");
+        //内存Builder方式
+        merchants.inMemory()
+            // .ali()
+            // .detailsId("1")
+            // .appid("appid")
+            // //应用私钥
+            // .keyPrivate(aliPayProperty.getKeyPrivate())
+            // //支付宝公钥
+            // .keyPublic(aliPayProperty.getKeyPublic())
+            // .inputCharset("utf-8")
+            // .notifyUrl(aliPayProperty.getDomain()+"/PayDemo/payBack1.json")
+            // //.returnUrl("http://pay.egzosn.com/PayDemo/payBack1.json")
+            // .pid(aliPayProperty.getPid())
+            // .seller(aliPayProperty.getSeller())
+            // .signType("RSA2")
+            // .test(true)
+            // .and()
+            .wx()
+            .detailsId("2")
+            .appid(wechatPayConfig.getAppid())
+            .mchId(wechatPayConfig.getMchId())
+            .secretKey(wechatPayConfig.getSecretKey())
+            .notifyUrl(wechatPayConfig.getDomain()+"/PayDemo/payBack2.json")
+            //.returnUrl("http://pay.egzosn.com/PayDemo/payBack2.json")
+            .inputCharset("utf-8")
+            .signType("MD5")
+            //设置请求相关的配置
+            .httpConfigStorage(wxHttpConfigStorage)
+            .test(false)
+            .and()
+            .wx()
+            .detailsId("3")
+            .appid(wechatPayConfig.getAppid())
+            .mchId("1603931736")
+            .secretKey("j8kPoAA3lBriQks9x0UK87IFqidPMMGi")
+            .notifyUrl(wechatPayConfig.getDomain()+"/PayDemo/payBack3.json")
+            //.returnUrl("http://pay.egzosn.com/PayDemo/payBack2.json")
+            .inputCharset("utf-8")
+            .signType("MD5")
+            //设置请求相关的配置
+            .httpConfigStorage(wxHttpConfigStorage)
+            .test(false)
+            .and()
+        ;
+    }
+
+    @Override
+    public void configure(PayMessageConfigurer payMessageConfigurer) {
+
+    }
+}

+ 33 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/config/ThirdPayBaseConfig.java

@@ -0,0 +1,33 @@
+package org.dromara.backstage.payment.config;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+
+/**
+ * @ClassName ThirdPayBaseConfig
+ * @Description 第三方支付基础类配置
+ * @Author luoyibo
+ * @Date 2025-01-14 22:52
+ * @Version 1.0
+ * @since jdk17
+ */
+@Data
+public class ThirdPayBaseConfig {
+    /**
+     * 当前支付使用的域名
+     */
+    @Value("${pay.domain}")
+    private String domain;
+
+    /**
+     * 二维码储存路径
+     */
+    @Value("${pay.qrCodeDir}")
+    private String qrCodeDir;
+
+    /**
+     * 嵌入二维码的图片路径(可选)
+     */
+    @Value("${pay.qrCodeEmbed}")
+    private String qrCodeEmbed;
+}

+ 39 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/config/WechatPayConfig.java

@@ -0,0 +1,39 @@
+package org.dromara.backstage.payment.config;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * name: WechatPayConfig
+ * package: org.dromara.backstage.config
+ * description: 微信支付配置
+ * date: 2025-01-13 19:19:53 19:19
+ *
+ * @author luoyibo
+ * @version 0.1
+ * @since JDK 1.8
+ */
+@ConfigurationProperties(prefix = "pay.wx")
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Component
+public class WechatPayConfig extends ThirdPayBaseConfig{
+    /**
+     * 微信公众号Id
+     */
+    private String appid;
+    /**
+     * 公众号秘钥
+     */
+    private String appSecret;
+    /**
+     * 合作者id(商户号)
+     */
+    private String mchId;
+    /**
+     * 商户api秘钥
+     */
+    private String secretKey;
+}

+ 1 - 2
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/controller/ThirdPayController.java

@@ -2,7 +2,6 @@ package org.dromara.backstage.payment.controller;
 
 import cn.hutool.core.date.DateUtil;
 import lombok.RequiredArgsConstructor;
-import org.apache.commons.lang.time.DateFormatUtils;
 import org.dromara.backstage.business.payments.ThirdPayBusiness;
 import org.dromara.backstage.payment.domain.bo.PayOrderBo;
 import org.dromara.backstage.payment.domain.vo.PayOrderVo;
@@ -29,7 +28,7 @@ import org.springframework.web.bind.annotation.RestController;
 @RequiredArgsConstructor
 @RestController
 @ResponseResult
-@RequestMapping("/third-pay")
+@RequestMapping("/pay")
 public class ThirdPayController extends BaseController {
     private final ThirdPayBusiness thirdPayBusiness;
     /**

+ 75 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/handlers/WxPayMessageHandler.java

@@ -0,0 +1,75 @@
+package org.dromara.backstage.payment.handlers;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.json.JSONUtil;
+import com.egzosn.pay.common.api.PayMessageHandler;
+import com.egzosn.pay.common.api.PayService;
+import com.egzosn.pay.common.bean.PayOutMessage;
+import com.egzosn.pay.common.exception.PayErrorException;
+import com.egzosn.pay.wx.bean.WxPayMessage;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.backstage.business.payments.ThirdPayBusiness;
+import org.dromara.backstage.payment.domain.bo.PayOrderBo;
+import org.dromara.backstage.payment.domain.bo.PurseInOutBo;
+import org.dromara.backstage.payment.domain.vo.PayOrderVo;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.enums.CreditStatusEnum;
+import org.dromara.common.core.enums.PayStatusEnum;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * 微信支付回调处理器 Created by ZaoSheng on 2016/6/1.
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class WxPayMessageHandler implements PayMessageHandler<WxPayMessage, PayService> {
+
+    private final ThirdPayBusiness thirdPayBusiness;
+
+    @Override
+    public PayOutMessage handle(WxPayMessage payMessage, Map<String, Object> context, PayService payService)
+        throws PayErrorException {
+        // 交易状态
+        if ("SUCCESS".equals(payMessage.getPayMessage().get("result_code"))) {
+            PayOrderBo bo = new PayOrderBo();
+            bo.setOrderId(Long.valueOf(payMessage.getOutTradeNo()));
+            bo.setPaySn(payMessage.getTransactionId());
+            bo.setCreditTime(payMessage.getTimeEnd());
+            bo.setPayStatus(PayStatusEnum.SUCCESS.code().toString());
+            // 1.更新订单支付状态
+            R<PayOrderVo> result = thirdPayBusiness.setOrderPayResult(bo);
+            if (R.isError(result)) {
+                log.error("[更新订单支付状态失败]-[{}]", JSONUtil.toJsonStr(bo));
+                return payService.getPayOutMessage("FAIL", "失败");
+            }
+            PayOrderBo postBo = BeanUtil.copyProperties(result.getData(), PayOrderBo.class);
+            // 2.订单入账一卡通账户
+            R<PurseInOutBo> postResult = thirdPayBusiness.postThirdPayOrder(postBo);
+            if (R.isError(postResult)) {
+                log.error("[订单入账失败]-[{}]", JSONUtil.toJsonStr(postBo));
+                return payService.getPayOutMessage("FAIL", "失败");
+            }
+            // 3.更新订单的入账状态
+            if (R.isSuccess(postResult)) {
+                // 更新支付订单表的入账状态
+                PayOrderBo updateBo = new PayOrderBo();
+                updateBo.setOrderId(postBo.getOrderId());
+                updateBo.setOriginalId(postResult.getData().getOriginalId());
+                updateBo.setCreditStatus(CreditStatusEnum.SUCCESS.code().toString());
+
+                result = thirdPayBusiness.setOrderPostResult(updateBo);
+                if (R.isError(result)) {
+                    log.error("[更新订单入账状态失败]-[{}]", JSONUtil.toJsonStr(postBo));
+                    return payService.getPayOutMessage("FAIL", "失败");
+                }
+            }
+            return payService.getPayOutMessage("SUCCESS", "OK");
+        } else {
+            return payService.getPayOutMessage("FAIL", "失败");
+        }
+    }
+}

+ 87 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/util/BufferedImageLuminanceSource.java

@@ -0,0 +1,87 @@
+package org.dromara.backstage.payment.util;
+
+import com.google.zxing.LuminanceSource;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+
+public class BufferedImageLuminanceSource extends LuminanceSource {
+
+	private final BufferedImage image;
+	private final int left;
+	private final int top;
+
+	public BufferedImageLuminanceSource(BufferedImage image) {
+		this(image, 0, 0, image.getWidth(), image.getHeight());
+	}
+
+	public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height) {
+		super(width, height);
+
+		int sourceWidth = image.getWidth();
+		int sourceHeight = image.getHeight();
+		if (left + width > sourceWidth || top + height > sourceHeight) {
+			throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
+		}
+
+		for (int y = top; y < top + height; y++) {
+			for (int x = left; x < left + width; x++) {
+				if ((image.getRGB(x, y) & 0xFF000000) == 0) {
+					image.setRGB(x, y, 0xFFFFFFFF); // = white
+				}
+			}
+		}
+
+		this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY);
+		this.image.getGraphics().drawImage(image, 0, 0, null);
+		this.left = left;
+		this.top = top;
+	}
+
+	public byte[] getRow(int y, byte[] row) {
+		if (y < 0 || y >= getHeight()) {
+			throw new IllegalArgumentException("Requested row is outside the image: " + y);
+		}
+		int width = getWidth();
+		if (row == null || row.length < width) {
+			row = new byte[width];
+		}
+		image.getRaster().getDataElements(left, top + y, width, 1, row);
+		return row;
+	}
+
+	public byte[] getMatrix() {
+		int width = getWidth();
+		int height = getHeight();
+		int area = width * height;
+		byte[] matrix = new byte[area];
+		image.getRaster().getDataElements(left, top, width, height, matrix);
+		return matrix;
+	}
+
+	public boolean isCropSupported() {
+		return true;
+	}
+
+	public LuminanceSource crop(int left, int top, int width, int height) {
+		return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height);
+	}
+
+	public boolean isRotateSupported() {
+		return true;
+	}
+
+	public LuminanceSource rotateCounterClockwise() {
+		int sourceWidth = image.getWidth();
+		int sourceHeight = image.getHeight();
+		AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth);
+		BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY);
+		Graphics2D g = rotatedImage.createGraphics();
+		g.drawImage(image, transform, null);
+		g.dispose();
+		int width = getWidth();
+		return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width);
+	}
+
+}

+ 70 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/util/Img2Base64Util.java

@@ -0,0 +1,70 @@
+package org.dromara.backstage.payment.util;
+
+import org.apache.commons.codec.binary.Base64;
+
+import java.io.*;
+
+/**
+ * 将图片转换为Base64<br>
+ * 将base64编码字符串解码成img图片
+ *
+ * @创建时间 2015-06-01 15:50
+ *
+ */
+public class Img2Base64Util {
+
+	/**
+	 * 将图片转换成Base64编码
+	 *
+	 * @param imgFile 待处理图片
+	 * @return
+	 */
+	public static String getImgStr(String imgFile) {
+//将图片文件转化为字节数组字符串,并对其进行Base64编码处理
+
+		InputStream in = null;
+		byte[] data = null;
+//读取图片字节数组
+		try {
+			in = new FileInputStream(imgFile);
+			data = new byte[in.available()];
+			in.read(data);
+			in.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return new String(Base64.encodeBase64(data));
+	}
+
+	/**
+	 * 对字节数组字符串进行Base64解码并生成图片
+	 *
+	 * @param imgStr      图片数据
+	 * @param imgFilePath 保存图片全路径地址
+	 * @return
+	 */
+	public static boolean generateImage(String imgStr, String imgFilePath) {
+//
+		if (imgStr == null) // 图像数据为空
+			return false;
+
+		try {
+//Base64解码
+			byte[] b = Base64.decodeBase64(imgStr);
+			for (int i = 0; i < b.length; ++i) {
+				if (b[i] < 0) {// 调整异常数据
+					b[i] += 256;
+				}
+			}
+//生成jpeg图片
+
+			OutputStream out = new FileOutputStream(imgFilePath);
+			out.write(b);
+			out.flush();
+			out.close();
+			return true;
+		} catch (Exception e) {
+			return false;
+		}
+	}
+}

+ 147 - 0
ruoyi-modules/ruoyi-backstage/src/main/java/org/dromara/backstage/payment/util/QRCodeUtil.java

@@ -0,0 +1,147 @@
+package org.dromara.backstage.payment.util;
+
+import com.google.zxing.*;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.OutputStream;
+import java.util.Hashtable;
+
+public class QRCodeUtil {
+	private static final String CHARSET = "utf-8";
+	private static final String FORMAT_NAME = "JPG";
+	// 二维码尺寸
+	private static final int QRCODE_SIZE = 300;
+	// LOGO宽度
+	private static final int WIDTH = 60;
+	// LOGO高度
+	private static final int HEIGHT = 60;
+
+	private static BufferedImage createImage(String content, String imgPath, boolean needCompress) throws Exception {
+		Hashtable hints = new Hashtable();
+		hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+		hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
+		hints.put(EncodeHintType.MARGIN, 1);
+		BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,
+				hints);
+		int width = bitMatrix.getWidth();
+		int height = bitMatrix.getHeight();
+		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+		for (int x = 0; x < width; x++) {
+			for (int y = 0; y < height; y++) {
+				image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
+			}
+		}
+		if (imgPath == null || "".equals(imgPath)) {
+			return image;
+		}
+		// 插入图片
+		QRCodeUtil.insertImage(image, imgPath, needCompress);
+		return image;
+	}
+
+	private static void insertImage(BufferedImage source, String imgPath, boolean needCompress) throws Exception {
+		File file = new File(imgPath);
+		if (!file.exists()) {
+			return;
+		}
+		Image src = ImageIO.read(new File(imgPath));
+		int width = src.getWidth(null);
+		int height = src.getHeight(null);
+		if (needCompress) { // 压缩LOGO
+			if (width > WIDTH) {
+				width = WIDTH;
+			}
+			if (height > HEIGHT) {
+				height = HEIGHT;
+			}
+			Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
+			BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+			Graphics g = tag.getGraphics();
+			g.drawImage(image, 0, 0, null); // 绘制缩小后的图
+			g.dispose();
+			src = image;
+		}
+		// 插入LOGO
+		Graphics2D graph = source.createGraphics();
+		int x = (QRCODE_SIZE - width) / 2;
+		int y = (QRCODE_SIZE - height) / 2;
+		graph.drawImage(src, x, y, width, height, null);
+		Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
+		graph.setStroke(new BasicStroke(3f));
+		graph.draw(shape);
+		graph.dispose();
+	}
+
+	public static void encode(String content, String imgPath, String destPath, boolean needCompress) throws Exception {
+		BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
+		mkdirs(destPath);
+		// String file = new Random().nextInt(99999999)+".jpg";
+		// ImageIO.write(image, FORMAT_NAME, new File(destPath+"/"+file));
+		ImageIO.write(image, FORMAT_NAME, new File(destPath));
+	}
+
+	public static BufferedImage encode(String content, String imgPath, boolean needCompress) throws Exception {
+		BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
+		return image;
+	}
+
+	public static void mkdirs(String destPath) {
+		File file = new File(destPath);
+		// 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常)
+		if (!file.exists() && !file.isDirectory()) {
+			file.mkdirs();
+		}
+	}
+
+	public static void encode(String content, String imgPath, String destPath) throws Exception {
+		QRCodeUtil.encode(content, imgPath, destPath, false);
+	}
+	// 被注释的方法
+	/*
+	 * public static void encode(String content, String destPath, boolean
+	 * needCompress) throws Exception { QRCodeUtil.encode(content, null, destPath,
+	 * needCompress); }
+	 */
+
+	public static void encode(String content, String destPath) throws Exception {
+		QRCodeUtil.encode(content, null, destPath, false);
+	}
+
+	public static void encode(String content, String imgPath, OutputStream output, boolean needCompress)
+			throws Exception {
+		BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
+		ImageIO.write(image, FORMAT_NAME, output);
+	}
+
+	public static void encode(String content, OutputStream output) throws Exception {
+		QRCodeUtil.encode(content, null, output, false);
+	}
+
+	public static String decode(File file) throws Exception {
+		BufferedImage image;
+		image = ImageIO.read(file);
+		if (image == null) {
+			return null;
+		}
+		BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
+		BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+		Result result;
+		Hashtable hints = new Hashtable();
+		hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
+		result = new MultiFormatReader().decode(bitmap, hints);
+		String resultStr = result.getText();
+		return resultStr;
+	}
+
+	public static String decode(String path) throws Exception {
+		return QRCodeUtil.decode(new File(path));
+	}
+
+}

+ 1 - 1
ruoyi-modules/ruoyi-backstage/src/main/resources/MET-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1,2 +1,2 @@
 org.dromara.backstage.config.LockConfig
-org.dromara.backstage.config.WechatPayConfig
+org.dromara.backstage.payment.config.WechatPayConfig