Althars123 3 年之前
父节点
当前提交
8255c6312c

+ 16 - 1
twzd-admin/src/main/java/com/miaxis/app/controller/wx/WxExtractController.java

@@ -6,6 +6,7 @@ import com.miaxis.common.core.page.ResponsePageInfo;
 import com.miaxis.common.exception.CustomException;
 import com.miaxis.common.utils.SecurityUtils;
 import com.miaxis.wx.domain.WxExtract;
+import com.miaxis.wx.dto.WxExtractBankDTO;
 import com.miaxis.wx.service.IWxExtractService;
 import com.miaxis.wx.service.IWxOrderService;
 import io.swagger.annotations.Api;
@@ -58,7 +59,7 @@ public class WxExtractController extends BaseController{
      */
     @PostMapping(value = "/withdraw")
     @ApiOperation("微信提现")
-    public Response getPrepareOrder(Integer amount) throws Exception{
+    public Response wxwithdrawal(Integer amount) throws Exception{
 
         if (amount == null){
             throw new CustomException("提现金额不能为空");
@@ -74,6 +75,20 @@ public class WxExtractController extends BaseController{
     }
 
 
+    /**
+     * 银行卡提现
+     */
+    @PostMapping(value = "/wxwithbankdrawal")
+    @ApiOperation("银行卡提现")
+    public Response wxwithbankdrawal(WxExtractBankDTO dto) throws Exception{
+
+
+        return Response.success(wxOrderService.wxwithbankdrawal(dto));
+
+
+    }
+
+
 
 
 }

+ 2 - 3
twzd-admin/src/main/java/com/miaxis/pc/controller/wx/WxForeverCodeController.java

@@ -11,7 +11,6 @@ import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
@@ -40,9 +39,9 @@ public class WxForeverCodeController extends BaseController{
             @ApiImplicitParam(name = "pageNum",value = "当前页码" ,dataType = "int", paramType = "query", required = false),
             @ApiImplicitParam(name = "pageSize",value = "每页数据量" , dataType = "int", paramType = "query", required = false),
     })
-    public ResponsePageInfo<WxForeverCode> list(@ModelAttribute WxForeverCode wxForeverCode){
+    public ResponsePageInfo<WxForeverCode> list(){
         startPage();
-        List<WxForeverCode> list = wxForeverCodeService.selectWxForeverCodeList(wxForeverCode);
+        List<WxForeverCode> list = wxForeverCodeService.selectWxForeverCodeList(new WxForeverCode());
         return toResponsePageInfo(list);
     }
 

+ 9 - 0
twzd-admin/src/main/resources/wechatpay/public.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtQpxN3gksreobgbu3Wy4
+4OV1KWVNtAE4q740vG2HlzO8pfEiDYqHD9vZ9nsiEBWjMFmiwsH/7HGMB1Fwt47i
+BmQa1u2G4ykZ25LHod2PRziq4Bh/ro7hiea5ZZwV0ol2weUEHaM2AcQNuPL1ynF3
+7y+C1IqIUbEqeAVFECs7W0U6ledT7t5ciatlh+SkfD6w4U7IAO+EBnJIluyNC6HF
+O3cxrxI2mEEgYkGLmXsMsDRVoZ27mbCHuPJZvdWJhybPG2InzrfLX6yPhyU//pTh
+K7ubjOu4nctHWc/POIofiZLZlgNARDy0feCmIxkTPzyRMk5uQ/whMckAtwAVpkuL
+3wIDAQAB
+-----END PUBLIC KEY-----

+ 52 - 42
twzd-admin/src/test/java/com/miaxis/test/NormalTest.java

@@ -1,27 +1,32 @@
 package com.miaxis.test;
 
+import com.github.wxpay.sdk.WXPay;
+import com.github.wxpay.sdk.WXPayConfig;
+import com.github.wxpay.sdk.WXPayConstants;
+import com.github.wxpay.sdk.WXPayUtil;
 import com.miaxis.TwzdApplication;
-import com.miaxis.feign.service.IWxSendService;
+import com.miaxis.common.config.WxPayConfigImpl;
+import com.miaxis.common.config.WxpayConfig;
+import com.miaxis.common.utils.XmlUtil;
 import com.miaxis.question.mapper.QuestionInfoMapper;
 import com.miaxis.question.vo.QuestionInfoExcelTypeVo;
 import com.miaxis.question.vo.QuestionInfoExcelVo;
-import com.miaxis.school.mapper.SchoolInfoMapper;
-import com.miaxis.user.service.IUserInfoService;
-import com.miaxis.wx.service.IWxForeverCodeService;
 import com.miaxis.wx.service.impl.WxGzhServiceImpl;
+import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.poi.hssf.usermodel.*;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit4.SpringRunner;
 
 import javax.annotation.Resource;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
+import java.io.*;
 import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 
 @ActiveProfiles("prod")
 @SpringBootTest(classes = TwzdApplication.class)
@@ -35,21 +40,11 @@ public class NormalTest {
     @Resource
     QuestionInfoMapper questionInfoMapper;
 
-    @Resource
-    IWxForeverCodeService wxForeverCodeService;
-
+    @Autowired
+    private WxpayConfig wxpayConfig;
 
-    @Resource
-    IWxSendService wxSendService;
 
-    @Resource
-    RedisTemplate redisTemplate;
 
-    @Resource
-    IUserInfoService userInfoService;
-
-    @Resource
-    SchoolInfoMapper schoolInfoMapper;
 
 
     @Value("${app.appid}")
@@ -58,32 +53,47 @@ public class NormalTest {
     @Value("${app.appSecret}")
     private String secret;
     @Test
-    public void getGzhToken() {
-//        QueryParams queryParams = new QueryParams();
-//        queryParams.setChoseColumName("city_name");
-//        List<String> cities = schoolInfoMapper.queryNames(queryParams);
-//        StringBuffer buffer = new StringBuffer();
-//        buffer.append("请选择驾校所在城市:").append("\n\n");
-//        for (String city: cities){
-//            buffer.append("<a href=\"weixin://bizmsgmenu?msgmenuid=1&msgmenucontent="+city+"\">"+city+"</a>").append("\n");
-//           // return MessageUtil.initText(fromUserName, toUserName, buffer.toString());
+    public void getGzhToken() throws Exception {
+        Map<String, String> sortMap = new TreeMap<String, String>();
+        sortMap.put("mch_id",wxpayConfig.getMerchantId());
+        sortMap.put("nonce_str", RandomStringUtils.randomAlphanumeric(32));
+        sortMap.put("sign_type","MD5");
+
+
+        WXPayConfig config = new WxPayConfigImpl();
+        String sign = WXPayUtil.generateSignature(sortMap, config.getKey(), WXPayConstants.SignType.MD5);
+        sortMap.put("sign", sign);
+        WXPay pay = new WXPay(config);
+
+        String url = "https://fraud.mch.weixin.qq.com/risk/getpublickey";
+        Map<String, String> resMap;
+        String xmlStr = pay.requestWithCert(url, sortMap, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs());
+        System.out.println(xmlStr);
+        resMap = XmlUtil.xmlToMap(xmlStr);
+        String publicStr = resMap.get("pub_key");
+        System.out.println(publicStr);
+        //保存到本地
+//        File file = new File("E:\\public.pem");
+//        if (!file.exists()){
+//            file.mkdir();
 //        }
-//        System.out.println(buffer);
-
-
+        FileWriter fwriter = null;
+        try {
+            // true表示不覆盖原来的内容,而是加到文件的后面。若要覆盖原来的内容,直接省略这个参数就好
+            fwriter = new FileWriter("E:\\public.pem", false);
+            fwriter.write(publicStr);
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        } finally {
+            try {
+                fwriter.flush();
+                fwriter.close();
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
     }
 
-//    @Test
-//    public void df() {
-//        UserInfo userInfo = userInfoService.getOne(new QueryWrapper<UserInfo>().eq("openid", fromUserName));
-//        //查询该用户的上级
-//        UserInfo upUserInfo = userInfoService.getUpUserInfo(fromUserName);
-//        //当用户销售类型为 普通用户(含驾校分成) 并且他的父级不是代理商(含驾校分成)时,且未填写驾校信息时则需要填写驾校信息
-//        if (userInfo.getSaleType() ==1 && upUserInfo.getSaleType()!=3 && StringUtils.isEmpty(userInfo.getSchoolName()) ){
-//            System.out.println("进来了");
-//        }
-//
-//    }
 
 
 

+ 27 - 0
twzd-common/src/main/java/com/miaxis/common/config/BeanConfig.java

@@ -5,13 +5,19 @@ import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
 import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
 import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
 import org.apache.http.client.HttpClient;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.bouncycastle.util.io.pem.PemReader;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.io.ClassPathResource;
 
 import java.io.File;
+import java.io.FileReader;
+import java.security.KeyFactory;
 import java.security.PrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.X509EncodedKeySpec;
 
 
 @Configuration
@@ -50,6 +56,27 @@ public class BeanConfig {
     }
 
 
+    /**
+     * 创建微信公钥 对象
+     * @return
+     * @throws Exception
+     */
+    @Bean
+    public RSAPublicKey readPublicKey() throws Exception {
+        KeyFactory factory = KeyFactory.getInstance("RSA");
+
+        File file = new ClassPathResource("wechatpay/public.pem").getFile();
+        try (FileReader keyReader = new FileReader(file);
+             PemReader pemReader = new PemReader(keyReader)) {
+
+            PemObject pemObject = pemReader.readPemObject();
+            byte[] content = pemObject.getContent();
+            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
+            return (RSAPublicKey) factory.generatePublic(pubKeySpec);
+        }
+    }
+
+
 
 
 }

+ 127 - 0
twzd-common/src/main/java/com/miaxis/common/utils/RSAUtils.java

@@ -0,0 +1,127 @@
+package com.miaxis.common.utils;
+
+
+
+import org.apache.commons.codec.binary.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.Cipher;
+import java.io.ByteArrayOutputStream;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+
+/**
+ * 描述:
+ *
+ * @author chen_q_i@163.com
+ * 2018/5/10 : 14:23.
+ * @version : 1.0
+ */
+@Component
+public class RSAUtils {
+    Logger log = LoggerFactory.getLogger(RSAUtils.class);
+    private static String RSA = "RSA";
+
+
+
+    @Autowired
+    private RSAPublicKey rsaPublicKey;
+
+    private static final int KEYLENGTH = 2048;
+    private static final int RESERVESIZE = 11;
+    /**
+     * 指定填充模式
+     */
+    private static final String CIPHERALGORITHM = "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING";
+
+
+    /**
+     * 用公钥加密 <br>
+     * 每次加密的字节数,不能超过密钥的长度值减去11
+     *
+     * @param plainBytes 需加密数据的byte数据
+     * @param publicKey  公钥
+     * @return 加密后的byte型数据
+     */
+    public String encrypt(byte[] plainBytes, PublicKey publicKey) throws Exception {
+        int keyByteSize = KEYLENGTH / 8;
+        int encryptBlockSize = keyByteSize - RESERVESIZE;
+        int nBlock = plainBytes.length / encryptBlockSize;
+        if ((plainBytes.length % encryptBlockSize) != 0) {
+            nBlock += 1;
+        }
+        ByteArrayOutputStream outbuf = null;
+        try {
+            Cipher cipher = Cipher.getInstance(CIPHERALGORITHM);
+            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+
+            outbuf = new ByteArrayOutputStream(nBlock * keyByteSize);
+            for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) {
+                int inputLen = plainBytes.length - offset;
+                if (inputLen > encryptBlockSize) {
+                    inputLen = encryptBlockSize;
+                }
+                byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen);
+                outbuf.write(encryptedBlock);
+            }
+            outbuf.flush();
+            byte[] encryptedData = outbuf.toByteArray();
+            return Base64.encodeBase64String(encryptedData);
+        } catch (Exception e) {
+            throw new Exception("ENCRYPT ERROR:", e);
+        } finally {
+            try {
+                if (outbuf != null) {
+                    outbuf.close();
+                }
+            } catch (Exception e) {
+                throw new Exception("CLOSE ByteArrayOutputStream ERROR:", e);
+            }
+        }
+    }
+
+
+
+
+
+
+    /***
+     * decode by Base64
+     */
+    public byte[] decodeBase64(String input) throws Exception {
+        return Base64.decodeBase64(input);
+
+    }
+
+    /**
+     * encodeBase64
+     */
+    public String encodeBase64(byte[] input) throws Exception {
+        return Base64.encodeBase64String(input);
+    }
+
+    /**
+     * 打印公钥信息
+     *
+     * @param publicKey
+     */
+    public  void printPublicKeyInfo(PublicKey publicKey) {
+        RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
+        log.info("----------RSAPublicKey----------");
+        log.info("Modulus.length=" + rsaPublicKey.getModulus().bitLength());
+        log.info("Modulus=" + rsaPublicKey.getModulus().toString());
+        log.info("PublicExponent.length=" + rsaPublicKey.getPublicExponent().bitLength());
+        log.info("PublicExponent=" + rsaPublicKey.getPublicExponent().toString());
+    }
+
+    public String encryptData(String data) throws Exception {
+        return encrypt(data.getBytes("UTF-8"), rsaPublicKey);
+    }
+
+
+
+}
+

+ 26 - 0
twzd-service/src/main/java/com/miaxis/wx/dto/WxExtractBankDTO.java

@@ -0,0 +1,26 @@
+package com.miaxis.wx.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class WxExtractBankDTO {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "银行编号",required = true)
+    private String bankCode;
+
+    @ApiModelProperty(value = "银行卡",required = true)
+    private String encBankNo;
+
+    @ApiModelProperty(value = "收款人姓名",required = true)
+    private String encTrueName;
+
+    @ApiModelProperty(value = "提现金额",required = true)
+    private Integer amount;
+
+
+
+
+
+}

+ 3 - 0
twzd-service/src/main/java/com/miaxis/wx/service/IWxOrderService.java

@@ -3,6 +3,7 @@ package com.miaxis.wx.service;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.miaxis.wx.domain.WxOrder;
 import com.miaxis.wx.dto.QuerySchoolOrderListDTO;
+import com.miaxis.wx.dto.WxExtractBankDTO;
 
 import java.util.List;
 
@@ -20,4 +21,6 @@ public interface IWxOrderService extends IService<WxOrder>{
     List<WxOrder> QuerySchoolOrderList(QuerySchoolOrderListDTO dto);
 
     String wxwithdrawal(Integer amount) throws Exception;
+
+    String wxwithbankdrawal(WxExtractBankDTO amount) throws Exception;
 }

+ 81 - 0
twzd-service/src/main/java/com/miaxis/wx/service/impl/WxOrderServiceImpl.java

@@ -9,6 +9,7 @@ import com.miaxis.common.config.WxPayConfigImpl;
 import com.miaxis.common.config.WxpayConfig;
 import com.miaxis.common.core.domain.entity.UserInfo;
 import com.miaxis.common.exception.CustomException;
+import com.miaxis.common.utils.RSAUtils;
 import com.miaxis.common.utils.SecurityUtils;
 import com.miaxis.common.utils.XmlUtil;
 import com.miaxis.system.service.ISysConfigService;
@@ -16,6 +17,7 @@ import com.miaxis.user.service.IUserInfoService;
 import com.miaxis.wx.domain.WxExtract;
 import com.miaxis.wx.domain.WxOrder;
 import com.miaxis.wx.dto.QuerySchoolOrderListDTO;
+import com.miaxis.wx.dto.WxExtractBankDTO;
 import com.miaxis.wx.mapper.WxOrderMapper;
 import com.miaxis.wx.service.IWxExtractService;
 import com.miaxis.wx.service.IWxOrderService;
@@ -27,6 +29,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
+import java.security.PublicKey;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
@@ -61,6 +64,10 @@ public class WxOrderServiceImpl extends ServiceImpl<WxOrderMapper, WxOrder> impl
     @Autowired
     private WxpayConfig wxpayConfig;
 
+
+    @Autowired
+    private RSAUtils rsaUtils;
+
     @Value("${app.appid}")
     private String appid;
 
@@ -131,4 +138,78 @@ public class WxOrderServiceImpl extends ServiceImpl<WxOrderMapper, WxOrder> impl
 
         return resMap.get("return_msg");
     }
+
+    @Override
+    public String wxwithbankdrawal(WxExtractBankDTO dto) throws Exception {
+        UserInfo student = SecurityUtils.getLoginUser().getStudent();
+        UserInfo dbUserInfo = userInfoService.getById(student.getId());
+        if (dbUserInfo.getProfitPrice() < dto.getAmount()){
+            throw new CustomException("提现金额不能大于可提现金额");
+        }
+
+        Map<String, String> sortMap = new TreeMap<String, String>();
+        sortMap.put("mch_id",wxpayConfig.getMerchantId());
+        sortMap.put("nonce_str", RandomStringUtils.randomAlphanumeric(32));
+        sortMap.put("partner_trade_no",getOrderCode(null));
+        sortMap.put("amount",String.valueOf(dto.getAmount()));
+        sortMap.put("desc","用户银行卡提现");
+
+
+//        X509Certificate validCertificate = verifier.getValidCertificate();
+        String rsa ="RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING";
+        //PublicKey publicKey = getPublicKey();
+
+        // 进行签名服务
+
+        sortMap.put("enc_true_name",rsaUtils.encryptData(dto.getEncTrueName()) );
+        sortMap.put("enc_bank_no",rsaUtils.encryptData(dto.getEncBankNo()));
+        sortMap.put("bank_code",dto.getBankCode());
+
+
+        WXPayConfig config = new WxPayConfigImpl();
+        String sign = WXPayUtil.generateSignature(sortMap, config.getKey(), WXPayConstants.SignType.MD5);
+        sortMap.put("sign", sign);
+        WXPay pay = new WXPay(config);
+
+        String url = "https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank";
+        Map<String, String> resMap;
+        String xmlStr = pay.requestWithCert(url, sortMap, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs());
+        resMap = XmlUtil.xmlToMap(xmlStr);
+        if (!"SUCCESS".equals(resMap.get("return_code"))||!"SUCCESS".equals(resMap.get("result_code"))){
+            throw new CustomException(resMap.get("return_msg"));
+        }
+        //新增提现记录
+        WxExtract wxExtract = new WxExtract();
+        wxExtract.setOpenid(student.getOpenid());
+        wxExtract.setExtractPrice(dto.getAmount());
+        wxExtract.setPartnerTradeNo(resMap.get("partner_trade_no"));
+        wxExtract.setStatus("1");
+        wxExtractService.save(wxExtract);
+
+        //更新余额
+        dbUserInfo.setProfitPrice(dbUserInfo.getProfitPrice()-dto.getAmount());
+        userInfoService.updateById(dbUserInfo);
+
+        return resMap.get("return_msg");
+    }
+
+    private PublicKey getPublicKey() throws Exception {
+        Map<String, String> sortMap = new TreeMap<String, String>();
+        sortMap.put("mch_id",wxpayConfig.getMerchantId());
+        sortMap.put("nonce_str", RandomStringUtils.randomAlphanumeric(32));
+        sortMap.put("sign_type","MD5");
+
+
+        WXPayConfig config = new WxPayConfigImpl();
+        String sign = WXPayUtil.generateSignature(sortMap, config.getKey(), WXPayConstants.SignType.MD5);
+        sortMap.put("sign", sign);
+        WXPay pay = new WXPay(config);
+
+        String url = "https://fraud.mch.weixin.qq.com/risk/getpublickey";
+        Map<String, String> resMap;
+        String xmlStr = pay.requestWithCert(url, sortMap, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs());
+        resMap = XmlUtil.xmlToMap(xmlStr);
+        return  null;
+
+    }
 }