jiangqs
2023-06-19 cd8d65c1e5378bddded25cad300f34024ba136ba
进件管理
8个文件已修改
18个文件已添加
1396 ■■■■■ 已修改文件
ruoyi-modules/ruoyi-goods/src/main/java/com/ruoyi/goods/controller/miniapp/AppGoodsController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-goods/src/main/java/com/ruoyi/goods/controller/miniapp/AppHomeController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/pom.xml 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/controller/management/MgtShopController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/dto/MgtShopAuthDto.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/pojo/shop/ShopAuthentication.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/vo/MgtShopAuthGetVo.java 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/impl/shop/ShopServiceImpl.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/shop/ShopService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/Certificate.java 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/CertificateUtils.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/SignUtils.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/WxShopUtils.java 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/WxUploadUtils.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/AccountInfo.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/BusinessLicenseInfo.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/CertificateItem.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/ContactInfo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/EncryptedCertificateItem.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/IdCardInfo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/IdDocInfo.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/OrganizationCertInfo.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/PlainCertificateItem.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/SalesSceneInfo.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/SubmitInfo.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-goods/src/main/java/com/ruoyi/goods/controller/miniapp/AppGoodsController.java
@@ -1,7 +1,6 @@
package com.ruoyi.goods.controller.miniapp;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.goods.domain.dto.AppGoodsInfoGetDto;
@@ -11,7 +10,6 @@
import com.ruoyi.goods.domain.vo.AppShoppingCartVo;
import com.ruoyi.goods.service.goods.GoodsService;
import com.ruoyi.goods.service.goods.ShoppingCartService;
import com.ruoyi.system.api.constant.AppErrorConstant;
import com.ruoyi.system.api.domain.dto.AppBaseBathDto;
import com.ruoyi.system.api.domain.poji.member.Member;
import com.ruoyi.system.api.service.RemoteMemberService;
@@ -59,7 +57,8 @@
            }
        }
        if(appGoodsInfoGetDto.getShopId()==null){
            throw new ServiceException(AppErrorConstant.NO_POSITION);
            //throw new ServiceException(AppErrorConstant.NO_POSITION);
            appGoodsInfoGetDto.setShopId(1L);
        }
        AppGoodsInfoVo appGoodsInfoVo = goodsService.getGoodsInfo(appGoodsInfoGetDto);
        return R.ok(appGoodsInfoVo);
ruoyi-modules/ruoyi-goods/src/main/java/com/ruoyi/goods/controller/miniapp/AppHomeController.java
@@ -59,7 +59,8 @@
            shopId = appRecommendGoodsPageDto.getPositionShopId();
        }
        if(shopId==null){
            throw new ServiceException(AppErrorConstant.NO_POSITION);
            //throw new ServiceException(AppErrorConstant.NO_POSITION);
            shopId = 1L;
        }
        Page<AppSimpleGoodsVo> page = new Page<>();
        page.setSize(appRecommendGoodsPageDto.getPageSize());
ruoyi-modules/ruoyi-order/pom.xml
@@ -104,7 +104,7 @@
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-java</artifactId>
            <version>0.2.7</version>
            <version>0.2.8</version>
        </dependency>
    </dependencies>
ruoyi-modules/ruoyi-shop/pom.xml
@@ -102,7 +102,42 @@
            <version>2.3.3</version>
        </dependency>
        <!-- 微信 -->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.9</version>
        </dependency>
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-java</artifactId>
            <version>0.2.8</version>
        </dependency>
        <dependency>
            <groupId>com.github.javen205</groupId>
            <artifactId>IJPay-WxPay</artifactId>
            <version>2.9.7-17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-rs-client</artifactId>
            <version>4.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>4.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxrs</artifactId>
            <version>4.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-rs-extension-providers</artifactId>
            <version>4.0.2</version>
        </dependency>
    </dependencies>
    <build>
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/controller/management/MgtShopController.java
@@ -169,7 +169,7 @@
    }
    @RequestMapping(value = "/mgtAuditShopCertificate", method = RequestMethod.POST)
    @ApiOperation(value = "平台编辑会员建议标签")
    @ApiOperation(value = "平台审核商户证书")
    public R mgtAuditShopCertificate(@RequestBody MgtAuditShopCertificateDto mgtAuditShopCertificateDto) {
        Long userId = SecurityUtils.getUserId();
        mgtAuditShopCertificateDto.setUserId(userId);
@@ -196,4 +196,13 @@
        List<MgtShopAuthPageVo> mgtShopAuthPageVoList = shopService.pageMgtShopAuth(page,mgtShopAuthPageDto);
        return R.ok(page.setRecords(mgtShopAuthPageVoList));
    }
    @RequestMapping(value = "/mgtShopAuth", method = RequestMethod.POST)
    @ApiOperation(value = "平台商户进件")
    public R mgtShopAuth(@RequestBody MgtShopAuthDto mgtShopAuthDto) {
        Long userId = SecurityUtils.getUserId();
        mgtShopAuthDto.setUserId(userId);
        shopService.mgtShopAuth(mgtShopAuthDto);
        return R.ok();
    }
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/dto/MgtShopAuthDto.java
New file
@@ -0,0 +1,143 @@
package com.ruoyi.shop.domain.dto;
import com.ruoyi.system.api.domain.dto.MgtBaseDto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @ClassName Mgt
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 10:57
 * @Version 1.0
 */
@Data
public class MgtShopAuthDto extends MgtBaseDto {
    @ApiModelProperty(value = "审核id")
    private String authId;
    /**
     * 商户id
     */
    @ApiModelProperty(value = "商户id")
    private Long shopId;
    @ApiModelProperty(value = "商户名称")
    private String shopName;
    @ApiModelProperty("店主姓名")
    private String shopownerName;
    @ApiModelProperty("店主联系方式")
    private String shopownerPhone;
    /**
     * 主题类型1个人2企业
     */
    @ApiModelProperty(value = "主题类型1个人2企业")
    private Integer mainType;
    /**
     * 营业执照商户名称
     */
    @ApiModelProperty(value = "营业执照商户名称")
    private String blShopName;
    /**
     * 营业执照编号
     */
    @ApiModelProperty(value = "营业执照编号")
    private String blNumber;
    /**
     * 营业执照法人姓名
     */
    @ApiModelProperty(value = "营业执照法人姓名")
    private String blCorporateName;
    /**
     * 营业执照注册地址
     */
    @ApiModelProperty(value = "营业执照注册地址")
    private String blRegisteredAddress;
    /**
     * 营业执照期限
     */
    @ApiModelProperty(value = "营业执照期限")
    private String blBusinessDeanline;
    /**
     * 营业执照图片
     */
    @ApiModelProperty(value = "营业执照图片")
    private String blImage;
    /**
     * 法人姓名
     */
    @ApiModelProperty(value = "法人姓名")
    private String lpCorporateName;
    /**
     * 法人身份证号
     */
    @ApiModelProperty(value = "法人身份证号")
    private String lpIdCard;
    /**
     * 法人身份证开始日期
     */
    @ApiModelProperty(value = "法人身份证开始日期")
    private String lpIcStartDate;
    /**
     * 法人身份证有效日期
     */
    @ApiModelProperty(value = "法人身份证有效日期")
    private String lpIcEndDate;
    /**
     * 法人身份证正面
     */
    @ApiModelProperty(value = "法人身份证正面")
    private String lpIcFront;
    /**
     * 法人身份证背面
     */
    @ApiModelProperty(value = "法人身份证背面")
    private String lpIcBack;
    /**
     * 结算账户类型1对公2对私
     */
    @ApiModelProperty(value = "结算账户类型1对公2对私")
    private String settlementAccountType;
    /**
     * 开户行
     */
    @ApiModelProperty(value = "开户行")
    private String saBank;
    /**
     * 开户名称
     */
    @ApiModelProperty(value = "开户名称")
    private String saAccountName;
    /**
     * 开户所在省
     */
    @ApiModelProperty(value = "开户所在省")
    private String saBankProvinceCode;
    /**
     * 开户所在市
     */
    @ApiModelProperty(value = "开户所在市")
    private String saBankCityCode;
    /**
     * 开户行全称
     */
    @ApiModelProperty(value = "开户行全称")
    private String saBankName;
    /**
     * 银行卡号
     */
    @ApiModelProperty(value = "银行卡号")
    private String saNumber;
    @ApiModelProperty(value = "补充资料")
    private String baPics;
    @ApiModelProperty(value = "补充说明")
    private String baDesc;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/pojo/shop/ShopAuthentication.java
@@ -242,9 +242,9 @@
    private String adRejectReason;
    /**
     * 法人验证链接
1、当申请状态为
ACCOUNT_NEED_VERIFY,且通过系统校验的申请单,将返回链接。
2、建议将链接转为二维码展示,让商户法人用微信扫码打开,完成账户验证。
        1、当申请状态为
        ACCOUNT_NEED_VERIFY,且通过系统校验的申请单,将返回链接。
        2、建议将链接转为二维码展示,让商户法人用微信扫码打开,完成账户验证。
     */
    @TableField("legal_validation_url")
    private String legalValidationUrl;
@@ -252,7 +252,16 @@
    private Date createTime;
    @TableField("update_time")
    private Date updateTime;
    /**
     * 补充资料
     */
    @TableField("ba_pics")
    private String baPics;
    /**
     * 补充说明
     */
    @TableField("ba_desc")
    private String baDesc;
    @Override
    protected Serializable pkVal() {
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/vo/MgtShopAuthGetVo.java
New file
@@ -0,0 +1,152 @@
package com.ruoyi.shop.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @ClassName MgtShopAuthDto
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 10:21
 * @Version 1.0
 */
@Data
public class MgtShopAuthGetVo  {
    @ApiModelProperty(value = "审核id")
    private String authId;
    /**
     * 商户id
     */
    @ApiModelProperty(value = "商户id")
    private Long shopId;
    @ApiModelProperty(value = "商户名称")
    private String shopName;
    @ApiModelProperty("店主姓名")
    private String shopownerName;
    @ApiModelProperty("店主联系方式")
    private String shopownerPhone;
    /**
     * 审核状态
     1:CHECKING:资料校验中
     2:ACCOUNT_NEED_VERIFY:待账户验证
     3:AUDITING:审核中
     4:REJECTED:已驳回
     5:NEED_SIGN:待签约
     6:FINISH:完成
     7:FROZEN:已冻结
     8:CANCELED:已作废
     */
    @ApiModelProperty(value = "审核状态")
    private Integer auditStatus;
    /**
     * 主题类型1个人2企业
     */
    @ApiModelProperty(value = "主题类型1个人2企业")
    private Integer mainType;
    /**
     * 营业执照商户名称
     */
    @ApiModelProperty(value = "营业执照商户名称")
    private String blShopName;
    /**
     * 营业执照编号
     */
    @ApiModelProperty(value = "营业执照编号")
    private String blNumber;
    /**
     * 营业执照法人姓名
     */
    @ApiModelProperty(value = "营业执照法人姓名")
    private String blCorporateName;
    /**
     * 营业执照注册地址
     */
    @ApiModelProperty(value = "营业执照注册地址")
    private String blRegisteredAddress;
    /**
     * 营业执照期限
     */
    @ApiModelProperty(value = "营业执照期限")
    private String blBusinessDeanline;
    /**
     * 营业执照图片
     */
    @ApiModelProperty(value = "营业执照图片")
    private String blImage;
    /**
     * 法人姓名
     */
    @ApiModelProperty(value = "法人姓名")
    private String lpCorporateName;
    /**
     * 法人身份证号
     */
    @ApiModelProperty(value = "法人身份证号")
    private String lpIdCard;
    /**
     * 法人身份证开始日期
     */
    @ApiModelProperty(value = "法人身份证开始日期")
    private String lpIcStartDate;
    /**
     * 法人身份证有效日期
     */
    @ApiModelProperty(value = "法人身份证有效日期")
    private String lpIcEndDate;
    /**
     * 法人身份证正面
     */
    @ApiModelProperty(value = "法人身份证正面")
    private String lpIcFront;
    /**
     * 法人身份证背面
     */
    @ApiModelProperty(value = "法人身份证背面")
    private String lpIcBack;
    /**
     * 结算账户类型1对公2对私
     */
    @ApiModelProperty(value = "结算账户类型1对公2对私")
    private String settlementAccountType;
    /**
     * 开户行
     */
    @ApiModelProperty(value = "开户行")
    private String saBank;
    /**
     * 开户名称
     */
    @ApiModelProperty(value = "开户名称")
    private String saAccountName;
    /**
     * 开户所在省
     */
    @ApiModelProperty(value = "开户所在省")
    private String saBankProvinceCode;
    /**
     * 开户所在市
     */
    @ApiModelProperty(value = "开户所在市")
    private String saBankCityCode;
    /**
     * 开户行全称
     */
    @ApiModelProperty(value = "开户行全称")
    private String saBankName;
    /**
     * 银行卡号
     */
    @ApiModelProperty(value = "银行卡号")
    private String saNumber;
    @ApiModelProperty(value = "补充资料")
    private String baPics;
    @ApiModelProperty(value = "补充说明")
    private String baDesc;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/impl/shop/ShopServiceImpl.java
@@ -614,4 +614,19 @@
        MgtBulletinBoardVo mgtBulletinBoardVoShop = shopMapper.shopTotal();
        return mgtBulletinBoardVoShop;
    }
    /**
     * @description  商户进件
     * @author  jqs
     * @date    2023/6/19 11:02
     * @param mgtShopAuthDto
     * @return  void
     */
    @Override
    public void mgtShopAuth(MgtShopAuthDto mgtShopAuthDto){
        ShopAuthentication shopAuthentication = shopAuthenticationService.getById(mgtShopAuthDto.getAuthId());
        BeanUtils.copyProperties(mgtShopAuthDto , shopAuthentication);
        shopAuthentication.setAuditStatus(3);
        shopAuthenticationService.saveOrUpdate(shopAuthentication);
    }
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/shop/ShopService.java
@@ -169,4 +169,13 @@
    * @date 2023/6/18 17:01
    */
    MgtBulletinBoardVo boardShopTotal();
    /**
     * @description  商户进件
     * @author  jqs
     * @date    2023/6/19 11:02
     * @param mgtShopAuthDto
     * @return  void
     */
    void mgtShopAuth(MgtShopAuthDto mgtShopAuthDto);
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/Certificate.java
New file
@@ -0,0 +1,162 @@
package com.ruoyi.shop.util;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.shop.util.dto.CertificateItem;
import com.ruoyi.shop.util.dto.EncryptedCertificateItem;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
/**
 * @date 2020-03-18 15:06
 * @description
 */
@Slf4j
public class Certificate {
    private static final Logger logger = LoggerFactory.getLogger(Certificate.class);
    /**
     * 获取微信支付平台证书
     *
     * @param merchantId
     * @param timeout
     * @param serialNo
     * @param mchPrivateKeyPath
     * @param APIv3Key
     * @param savePath
     * @return
     */
    public static List<X509Certificate> getCertByAPI(String merchantId, int timeout, String serialNo, String mchPrivateKeyPath, String wechatPubKeyPath, String APIv3Key, String savePath) {
        String result = "";
        //创建http请求
        HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/certificates");
        httpGet.addHeader("Content-Type", "application/json");
        httpGet.addHeader("Accept", "application/json");
        String authorization = SignUtils.authorization("GET", "/v3/certificates", merchantId, serialNo, "", mchPrivateKeyPath);
        //设置认证信息
        httpGet.setHeader("Authorization", authorization);
        //设置请求器配置:如超时限制等
        RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout * 1000).setConnectTimeout(timeout * 1000).build();
        httpGet.setConfig(config);
        List<X509Certificate> x509Certs = new ArrayList<X509Certificate>();
        try {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            CloseableHttpResponse response = httpClient.execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();
            HttpEntity httpEntity = response.getEntity();
            result = EntityUtils.toString(httpEntity, "UTF-8");
            if (statusCode == 200) {
                logger.info("下载平台证书返回结果:" + result);
                Header[] timestampHeader = response.getHeaders("Wechatpay-Timestamp");
                Header[] nonceHeader = response.getHeaders("Wechatpay-Nonce");
                Header[] signatureHeader = response.getHeaders("Wechatpay-Signature");
                if (timestampHeader != null && timestampHeader.length > 0 &&
                        nonceHeader != null && nonceHeader.length > 0 &&
                        signatureHeader != null && signatureHeader.length > 0) {
                    // 验证微信支付返回签名
                    String wTimestamp = timestampHeader[0].getValue();
                    String wNonce = nonceHeader[0].getValue();
                    String wSign = signatureHeader[0].getValue();
                    logger.info("wTimestamp:{},wNonce:{},wSign:{}", wTimestamp, wNonce, wSign);
                    // 拼装待签名串
                    StringBuffer ss = new StringBuffer();
                    ss.append(wTimestamp).append("\n");
                    ss.append(wNonce).append("\n");
                    ss.append(result).append("\n");
                    // 验证签名
                    if (SignUtils.v3VerifyRSA(ss.toString(), Base64.decodeBase64(wSign.getBytes()), wechatPubKeyPath)) {
                        List<CertificateItem> certList = new ArrayList<CertificateItem>();
                        JSONObject json = JSONObject.parseObject(result);
                        logger.info("查询结果json字符串转证书List:" + json.get("data"));
                        JSONArray jsonArray = (JSONArray) json.get("data");
                        for (int i = 0; i < jsonArray.size(); i++) {
                            CertificateItem certificateItem = new CertificateItem();
                            EncryptedCertificateItem encryptCertificate = new EncryptedCertificateItem();
                            JSONObject bo = JSONObject.parseObject(jsonArray.get(i).toString());
                            certificateItem.setSerial_no(bo.get("serial_no").toString());
                            certificateItem.setEffective_time(bo.get("effective_time").toString());
                            certificateItem.setExpire_time(bo.get("expire_time").toString());
                            JSONObject encryptBo = JSONObject.parseObject(bo.get("encrypt_certificate").toString());
                            encryptCertificate.setAlgorithm(encryptBo.get("algorithm").toString());
                            encryptCertificate.setNonce(encryptBo.get("nonce").toString());
                            encryptCertificate.setAssociated_data(encryptBo.get("associated_data").toString());
                            encryptCertificate.setCiphertext(encryptBo.get("ciphertext").toString());
                            certificateItem.setEncrypt_certificate(encryptCertificate);
                            certList.add(certificateItem);
                        }
                        logger.info("证书List:" + certList);
                        /*List<PlainCertificateItem> plainList = decrypt(certList, APIv3Key);
                        if (CollectionUtils.isNotEmpty(plainList)) {
                            logger.info("平台证书开始保存");
                            x509Certs = saveCertificate(plainList, savePath);
                        }*/
                    }
                }
            }
            response.close();
            httpClient.close(); //throw
            return x509Certs;
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("下载平台证书返回结果:" + e);
        }
        return x509Certs;
    }
    /*private static List<PlainCertificateItem> decrypt(List<CertificateItem> certList,CloseableHttpResponse response) throws GeneralSecurityException, IOException {
        List<PlainCertificateItem> plainCertificateList = new ArrayList<PlainCertificateItem>();
        AesUtil aesUtil = new AesUtil("".getBytes(StandardCharsets.UTF_8));
        for(CertificateItem item:certList){
            PlainCertificateItem bo = new PlainCertificateItem();
            bo.setSerialNo(item.getSerial_no());
            bo.setEffectiveTime(item.getEffective_time());
            bo.setExpireTime(item.getExpire_time());
            logger.info("平台证书密文解密");
            bo.setPlainCertificate(aesUtil.decryptToString(item.getEncrypt_certificate().getAssociated_data().getBytes(StandardCharsets.UTF_8),
                    item.getEncrypt_certificate().getNonce().getBytes(StandardCharsets.UTF_8), item.getEncrypt_certificate().getCiphertext()));
            logger.info("平台证书公钥明文:"+bo.getPlainCertificate());
            plainCertificateList.add(bo);
        }
        return plainCertificateList;
    }*/
    //证书保存
    /*private static List<X509Certificate> saveCertificate(List<PlainCertificateItem> cert) throws IOException {
        List<X509Certificate> x509Certs = new ArrayList<X509Certificate>();
        File file = new File("平台证书路径");
        file.mkdirs();
        for (PlainCertificateItem item : cert) {
            ByteArrayInputStream inputStream = new ByteArrayInputStream(item.getPlainCertificate().getBytes(StandardCharsets.UTF_8));
            X509Certificate x509Cert = PemUtil.loadCertificate(inputStream);
            x509Certs.add(x509Cert);
            String outputAbsoluteFilename = file.getAbsolutePath() + File.separator + "wechatpay_" + item.getSerialNo() + ".pem";
            try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputAbsoluteFilename), StandardCharsets.UTF_8))) {
                writer.write(item.getPlainCertificate());
            }
            logger.info("输出证书文件目录:" + outputAbsoluteFilename);
        }
        return x509Certs;
    }*/
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/CertificateUtils.java
New file
@@ -0,0 +1,106 @@
package com.ruoyi.shop.util;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
/**
 * @date 2020-03-18 11:25
 * @description 证书相关
 */
public class CertificateUtils {
    /**
     * 获取商户私钥
     * @param priKeyPath 商户私钥证书路径
     * @return
     */
    public static String getPrivateKey(String priKeyPath) throws Exception {
        String originalKey = FileUtil.readUtf8String(priKeyPath);
        String privateKey = originalKey
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s+", "");
        return getPrivateKeyStr(loadPrivateKey(privateKey));
    }
    private static String getPrivateKeyStr(PrivateKey privateKey) {
        return Base64.encode(privateKey.getEncoded());
    }
    /**
     * 从字符串中加载私钥
     * @param privateKeyStr 私钥
     * @return
     */
    public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decode(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("私钥非法");
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空");
        }
    }
    /**
     * 获取证书
     * @param fileName 证书文件路径  (required)
     * @return
     */
    public static X509Certificate getCertificate(String fileName) throws IOException {
        InputStream fis = new FileInputStream(fileName);
        try (BufferedInputStream bis = new BufferedInputStream(fis)) {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书文件", e);
        }
    }
    /**
     * 获取证书
     * @param inputStream 证书文件
     * @return
     */
    public static X509Certificate getCertificate(InputStream inputStream) {
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书", e);
        }
    }
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/SignUtils.java
New file
@@ -0,0 +1,160 @@
package com.ruoyi.shop.util;
import cn.hutool.http.HttpResponse;
import com.ruoyi.common.core.utils.uuid.IdUtils;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.List;
import java.util.Map;
/**
 * @date 2020-03-18 11:02
 * @description 签名相关
 */
public class SignUtils {
    private static final Logger logger = LoggerFactory.getLogger(SignUtils.class);
    /**
     * 签名生成
     * @param method 请求方法 如POST
     * @param urlSuffix 请求地址后缀 如/v3/certificates
     * @param mchId 电商平台商户号
     * @param serialNo 电商平台商户API证书序列号
     * @param body 请求请求报文主体,如果没有,就传空字符串
     * @param mchPrivateKeyPath 电商平台商户API私钥
     * @return
     */
    public static String authorization(String method,String urlSuffix,String mchId,String serialNo,String body,String mchPrivateKeyPath) {
        try {
            String mchPrivateKey = CertificateUtils.getPrivateKey(mchPrivateKeyPath);
            //时间戳
            String timestamp = Long.toString(System.currentTimeMillis()/1000);
            //随机数
            String nonceStr = IdUtils.randomUUID();
            //拼签名串
            StringBuilder sb = signMessage(method,urlSuffix,timestamp,nonceStr,body);
            logger.info("sign original string:{}",sb.toString());
            //计算签名
            String sign = new String(Base64.encodeBase64(v3signRSA(sb.toString(),mchPrivateKey)));
            logger.info("sign result:{}",sign);
            //拼装http头的Authorization内容
            String authorization ="WECHATPAY2-SHA256-RSA2048 mchid=\""+mchId+"\",nonce_str=\""+nonceStr+"\",signature=\""+sign+"\",timestamp=\""+timestamp+"\",serial_no=\""+serialNo+"\"";
            logger.info("authorization result:{}",authorization);
            return authorization;
        } catch (Exception e) {
            logger.error("authorization Exception result:{}",e);
            e.printStackTrace();
            return null;
        }
    }
    /**
     * Authorization 签名串
     * @param method
     * @param urlSuffix
     * @param timestamp
     * @param nonceStr
     * @param body
     * @return
     */
    private static StringBuilder signMessage(String method,String urlSuffix,String timestamp,String nonceStr,String body) {
        return new StringBuilder()
                .append(method)
                .append("\n")
                .append(urlSuffix)
                .append("\n")
                .append(timestamp)
                .append("\n")
                .append(nonceStr)
                .append("\n")
                .append(body)
                .append("\n");
    }
    /**
     * 私钥签名
     * @param data 需要加密的数据
     * @param mchPriKey
     * @return
     * @throws Exception
     */
    public static byte[] v3signRSA(String data, String mchPriKey) throws Exception {
        //签名的类型
        Signature sign = Signature.getInstance("SHA256withRSA");
        //读取商户私钥,该方法传入商户私钥证书的内容即可
        byte[] keyBytes = Base64.decodeBase64(mchPriKey);
        PKCS8EncodedKeySpec keySpec =new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey priKey = keyFactory.generatePrivate(keySpec);
        sign.initSign(priKey);
        sign.update(data.getBytes(StandardCharsets.UTF_8));
        return sign.sign();
    }
    public static boolean v3VerifyRSA(HttpResponse response,String wechatPubKeyPath) {
        if (response == null || StringUtils.isEmpty(wechatPubKeyPath)) {
            return false;
        }
        Map<String, List<String>> headers = response.headers();
        //验证微信支付返回签名
        String headsTimestamp = headers.get("Wechatpay-Timestamp").get(0);
        String headsNonce = headers.get("Wechatpay-Nonce").get(0);
        String headsSign = headers.get("Wechatpay-Signature").get(0);
        String resContent = response.body();
        //拼装待签名串
        StringBuilder sb =new StringBuilder();
        sb.append(headsTimestamp).append("\n");
        sb.append(headsNonce).append("\n");
        sb.append(resContent).append("\n");
        try {
            //验证签名
            return v3VerifyRSA(sb.toString(), Base64.decodeBase64(headsSign.getBytes()), wechatPubKeyPath);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    public static boolean v3VerifyRSA(String data,byte[] sign, String wechatPubKeyPath) throws Exception{
        if(data == null || sign == null || wechatPubKeyPath == null){
            return false;
        }
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        FileInputStream in =new FileInputStream(wechatPubKeyPath);
        Certificate c = cf.generateCertificate(in);
        in.close();
        PublicKey publicKey = c.getPublicKey();
        Signature signature = Signature.getInstance("SHA256WithRSA");
        signature.initVerify(publicKey);
        signature.update(data.getBytes(StandardCharsets.UTF_8));
        boolean result = signature.verify(sign);
        if (result) {
            logger.info("v3VerifyRSA result:{}","签名验证成功");
        } else {
            logger.info("v3VerifyRSA result:{}","签名验证失败");
        }
        return result;
    }
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/WxShopUtils.java
New file
@@ -0,0 +1,211 @@
package com.ruoyi.shop.util;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.shop.util.dto.ContactInfo;
import com.ruoyi.shop.util.dto.IdCardInfo;
import com.ruoyi.shop.util.dto.SubmitInfo;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
/**
 * @ClassName WxShopUtils
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 11:11
 * @Version 1.0
 */
public class WxShopUtils {
    public void ApplymentSubMch() throws Exception {
        // 初始化httpClient
        CloseableHttpClient httpClient = createHttpClient();
        //请求URL
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/");
        // 请求body参数
        String reqdata = "{"
                + "\"business_info\": {"
                + "\"merchant_shortname\":\"上海XXX有限公司\","
                + "\"service_phone\":\"139XXXX\","
                + "\"sales_info\": {"
                + "\"mini_program_info\": {"
                + "\"mini_program_pics\": ["
                + "\"0\":\"F8O5MNAOlYZyxZcLnWXkyk72coZ3CVX3XobPZng-xepKPTrcH_XMfrGGYLi1XFogMG9P25LSUX6gjXU5iPI74YqiRUDfORyw2d76wK3vzuQ\""
                + "],"
                + "\"mini_program_sub_appid\":\"wx76c3f74c108876c2\""
                + "},"
                + "\"mp_info\": {"
                + "\"mp_pics\": ["
                + "\"0\":\"F8O5MNAOlYZyxZcLnWXkys5TBxf0dyfesnyfNuquBMArHRfgCq8hDRdscDpblCcEgvgV5MkVch9IBEX7aatZCmI8k5Wdb_M8a4bYcmlg6V8\""
                + "],"
                + "\"mp_sub_appid\":\"wx85c88c63bd22651f\""
                + "},"
                + "\"sales_scenes_type\": ["
                + "\"0\":\"SALES_SCENES_MP\","
                + "\"1\":\"SALES_SCENES_MINI_PROGRAM\""
                + "]"
                + "}"
                + "},"
                + "\"subject_info\": {"
                + "\"business_license_info\": {"
                + "\"legal_person\":\"江XX\","
                + "\"license_copy\":\"F8O5MNAOlYZyxZcLnWXkysZ5xo8fE3SHJ1pHUe6OJKmNIWtqUVyv4aY2-JLTgjDOSovWaKkBu3AbkjGuQXhesDzREu2pT_Yf_hxlFKPJMwQ\","
                + "\"merchant_name\":\"上海XXX有限公司\","
                + "\"license_number\":\"91310118MA1JXXXX\""
                + "},"
                + "\"subject_type\":\"SUBJECT_TYPE_ENTERPRISE\","
                + "\"identity_info\": {"
                + "\"owner\":true,"
                + "\"id_doc_type\":\"IDENTIFICATION_TYPE_IDCARD\","
                + "\"id_card_info\": {"
                + "\"id_card_copy\":\"F8O5MNAOlYZyxZcLnWXkyvLpqoz7MNRULxju0NqUCtLGBl_vm8Bhj9uAyuLyDART9UsA10spF9gbT3DEoM85HxPQOhMuilK0zLtOxAjPx58\","
                + "\"card_period_end\":\"2035-12-28\","
                + "\"card_period_begin\":\"2015-12-28\","
                + "\"id_card_name\":\"JRMJGA7B7oSVS+krFeaQn4AvYZE2LL9tTwA+XXa9lYJU4AhS+Qheb7fhgo+D+hfa0v+ykU1BVvVo7NpCfgOYQqmYDyYF27Q/p4hB7IoqZLhJUE+hTG45VNoa9ASaKG79Hg+eGz4YTIHFGMFm1ljyg4fd5lGJixCqn38/gCfwm5x6vDOMVkFpqbUdR7klQwReGoG7iSlEpr4gZPqzItcBHiuXidMToKZTX1Lol1Lx52DHk0blcdh1CMRhv+wxQzF0lJMy/WtVyjKuB8daDbLJKTEwDx5KfkTZGHmGiaJnWY0qFX03sXsufvfB0Wj6Z37+nJnV16fplPmdr1/KEHamLw==\","
                + "\"id_card_national\":\"F8O5MNAOlYZyxZcLnWXkyvcmQrkBXCIo2vZGUoM6ER-skL5k6IOhzeJ34tNNUggQIPfJbM_pzdg9jM4pQbkIm84_bKfi2JKNn99xIurNgwI\","
                + "\"id_card_number\":\"V0quwnIu/PLbdQQ3/nzMqjvHtChDqed4Jpc3Iy2lgnFoWVNXguRWBHMYKHaLgLvnxLvO6TciMnrSw13QzUJD4ZnEIiks8gGTd+OCzl5Jl3F+sneQ+Js4akBnCSBQso/SERvi6vUqCeRhX1vtiFsmKtqlL8r/ssrFev4FoJEka5gGME1osTgcpMMTIR8rolIO1oXbl3e8z5RDics5xKH2Ogjzis73yq9M18ap0uRRdLWoX/cxeG3tbU1bi5jZsAU9hGHXs4BKTaHfrslsKIkqXR0f2Jpk7H5qtwAyP8glcALN7gB5DIOmBEIZ5zx2PrDm38CsZHstu0hF8beXV/PG2w==\""
                + "}"
                + "}"
                + "},"
                + "\"business_code\":\"20201126100507000003\","
                + "\"contact_info\": {"
                + "\"contact_name\":\"AD2jwe8eYFYfeoSmBpyRYUawyQgQkUoAajOlHnQM/JzproDF7hikLDouMr5Mb1W5otvdJZYEipG0HzwLpG9NXbqP6SXSpzQFb8fY5cpuXZsIbDy70lAybF0bbxJiJw1PHSd7KpMbu0XHqwlicePLUOw7HCNV6euQsKEvtusiWsaVrNNdKXpj3ONhyAg2ws4ibc56GUVoNll3osmGLPi6/bIn3pD6sjNThgPE9UzRQax0XPwgIpafIewQiVy0GNtv5P33wqPa9pAsdEmaLXNtQr2Ddjw5SYMInen1qbVO8NP0VhuWmYiAjd1WbkNnR+gbwsilBCYYeRuFbKfqatASnA==\","
                + "\"mobile_phone\":\"GLc5+TuqJbxLeQO+CrKeDWF+7lZq9IsidIU4zcMpus4qsE8r1qNyvVMjrZfWLrjOItiQhXqkiKZs0B4M0UWAviWwmBhNWW0BJrauBjZbGZU+pLHC/2eOetdDC4sQqHN7Xw0N4tizqeQNu49n+DbSiv0TL09EcmNUUV9rImfjpVyo77MxKhPvdg3ODwS+IVvY2Tc9q11iBgaPIQ+XwoBaJGth1wdw0GLgEFUmX02TWxjHxq7f5O1RU3LjBlt1O+56ZZYpA+zr+IjQ/TwrIDp+4nk9v1oW2wtHuwrdqKrlLyJ1/zdoqwSddLuGxp8PwSM8CKobQP8TXhJu0S9pwLbNyg==\","
                + "\"contact_id_number\":\"iC35X6SaOx4A4BEJ4fmM2oOkYRO5rEzRh2QRdrxFD3+HwmNjxddsm1LoqfhSiV34DAO2x3Ded3q2c1LJ7NHqa3/IGCDss4FKlgIyiu5AmzQTTYwsobmvVFxeSUXA4PnH/tUTfatJOuj+9Emt7fmnpO9uWeCLIn5CXaaABrXpO4mUu8qPBaoF2jTfbwBm6QiYX58kATyRrIvBRs6VorHJydXkz3P1hCejh8sAYoJXlNHnoajI+NEwiWX9RhwUadYLbMwww68cc7Yzehp3s6an0NWuC76Y4e7FQ/i5D9ZpUvLKUoYfFsZMnHrv/PhLxmv5l8gQs9/IkmOdYfRujKZJwQ==\","
                + "\"contact_email\":\"ggK2ABtJ8bBiBo1iM7I6R2yEvhD6WC96d07gthnPGajc4lBCKpIqXjyNX8B97vc0OilyLRNUcySbT/KUejPyu4x9W6Ok6iS4zcerfKfIQkKs+3JXIMo0bu+GyI5O0fzRrPg1Mek2okfIjVK9wiTNJTzoGyz9twMXEaqqrOTbysyhnkqfrol5dad/TPg8qNWaBO3+Ma/hr1Tr0xeHWkKX3mBySC3NW49HM51x0PvFAwnStHr/8hiGzGpqAA3yJuYdlEwey3Lv+/bMmbTq+AyrYq9xX3q+n0VHuiSnzgICPwQiP4zXsoivA0bm7DSgcP1Oa6MjpGqMOS7AU1dHyak2GQ==\""
                + "},"
                + "\"bank_account_info\": {"
                + "\"account_number\":\"Bfu6CzrRWlVTLZwjEK2CCXG0KRfGoIsbYs9IYmtFcV9FTEVLCcVCRGW6DVE2hMMr7U9tSdo7QqsptXZ7Zhciandyx9kRkY/VmPywurySvCbqpOLbx3AgmQY7H6/Ye542R0uloi6Pdp5FjqY/TdnlDcTS4cqv9iqU0DqKAUt7q+L6jZ8lrSlGFPI/1RkXCGAhlTSZ/iAJYDWzcN/E0cno9BQPYCmEIWldzj7W3Tmhi1eaZYX5mlLWnTOuVmMsRXIehb4y6LCi6T0kS7/c3GOFwlTzHVaaUgFqZhOL00h2G4Cl94uZfuVNO7KTiFaWtV61muEN6JfaSnFwRDSHxhtsRg==\","
                + "\"bank_address_code\":\"310101\","
                + "\"account_name\":\"RVBRWZKlmzrjkDmz4uEkaLQLU5q5xErXGoWhzDNdnQjRlfn1NVHL2EtIE+B0BULFNOHrX61q13lcgo9CDal9YJ/LjZkzAAsivwJZpNSRZbAsNNjqbYiiVhdnxYpwMqtukVgZAFgx0XMovOeSGX1+Dosc2vKJSsxtUw6wJXcZbMjvsX5/xamFTvRIx1J2a/q3ODBJ9URO5O2FDhGwLx8LQnFIv7xUDM3JXw7c6+eNy1OYTVwlj3/MIDQVw2eiPjpG1vC6K6PRwCI4wFZk1QwH99Xa3Wwvf+ek54j7FQTh0N703dK6jjonL5L6Lh9ZsckLx45InkARlPnU0KQpoK5g9w==\","
                + "\"bank_account_type\":\"BANK_ACCOUNT_TYPE_CORPORATE\","
                + "\"account_bank\":\"招商银行\""
                + "},"
                + "\"settlement_info\": {"
                + "\"settlement_id\":\"716\","
                + "\"qualification_type\":\"零售批发/生活娱乐/网上商城/其他\""
                + "}"
                + "}";
        StringEntity entity = new StringEntity(reqdata,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Wechatpay-Serial", "XXXXXXXXXXXXXX");
        //完成签名并执行请求
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) { //处理成功
                System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
            } else if (statusCode == 204) { //处理成功,无返回Body
                System.out.println("success");
            } else {
                System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } finally {
            response.close();
        }
        httpClient.close();
    }
    /**
     * @description  创建httpClient
     * @author  jqs
     * @date    2023/6/19 12:50
     * @param
     * @return  CloseableHttpClient
     */
    private CloseableHttpClient createHttpClient() throws NotFoundException, IOException, GeneralSecurityException, HttpCodeException {
        String merchantId = "";
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                new FileInputStream("/path/to/apiclient_key.pem"));
        String merchantSerialNumber = "";
        String apiV3Key = "";
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
                new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
        // ... 若有多个商户号,可继续调用putMerchant添加商户信息
        // 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(merchantId);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }
    //提交申请
    /*public static String apply(SubmitInfo submitInfo) throws Exception {
        String bodyStr=convertToStr(submitInfo);
        HttpResponse response = HttpUtils.v3HttpExecute("POST",EcommerceUrl.APPLYMENTS,
                "自己系统的应用名称","服务商商户号","商户API证书序列号","平台证书序列号",requestParam,"商户API证书位置.pem",null);
    }*/
    //加密申请信息
    private static String convertToStr(SubmitInfo submitInfo) throws Exception {
        rsaEncryptSubmitInfo(submitInfo);
        return JSONObject.toJSONString(submitInfo);
    }
    private static void rsaEncryptSubmitInfo(SubmitInfo submitInfo) throws Exception {
        IdCardInfo idCardInfo=submitInfo.getId_card_info();
        if(idCardInfo!=null){
            idCardInfo.setId_card_name(rsaEncryptByCert(idCardInfo.getId_card_name()));
            idCardInfo.setId_card_number(rsaEncryptByCert(idCardInfo.getId_card_number()));
        }
        ContactInfo contactInfo=submitInfo.getContact_info();
        if(contactInfo!=null){
            contactInfo.setContact_name(rsaEncryptByCert(contactInfo.getContact_name()));
            contactInfo.setContact_id_card_number(rsaEncryptByCert(contactInfo.getContact_id_card_number()));
            contactInfo.setMobile_phone(rsaEncryptByCert(contactInfo.getMobile_phone()));
            if(!StringUtils.isEmpty(contactInfo.getContact_email())){
                contactInfo.setContact_email(rsaEncryptByCert(contactInfo.getContact_email()));
            }
        }
    }
    private static String rsaEncryptByCert(String content) throws Exception {
        String platPrivateKey = "";
        InputStream inStream=new ByteArrayInputStream(platPrivateKey.getBytes(StandardCharsets.UTF_8));
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate certificate = (X509Certificate)cf.generateCertificate(inStream);
        PublicKey publicKey=certificate.getPublicKey();
        Cipher ci= Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
        ci.init(Cipher.ENCRYPT_MODE,publicKey);
        return Base64.getEncoder().encodeToString(ci.doFinal(content.getBytes(StandardCharsets.UTF_8)));
    }
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/WxUploadUtils.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.shop.util;
/**
 * @ClassName WxUploadUtils
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 11:55
 * @Version 1.0
 */
public class WxUploadUtils {
    /*String filePath = "/your/home/hellokitty.png";
    URI uri = new URI("https://api.mch.weixin.qq.com/v3/merchant/media/upload");
    File file = new File(filePath);
    FileInputStream ins1 = new FileInputStream(file)) {
        String sha256 = DigestUtils.sha256Hex(ins1);
        try (InputStream ins2 = new FileInputStream(file)) {
            HttpPost request = new WechatPayUploadHttpPost.Builder(uri)
                    .withImage(file.getName(), sha256, ins2)
                    .build();
            CloseableHttpResponse response1 = httpClient.execute(request);
        }
    }*/
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/AccountInfo.java
New file
@@ -0,0 +1,35 @@
package com.ruoyi.shop.util.dto;
/**
 * @ClassName AccountInfo
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 12:55
 * @Version 1.0
 */
import lombok.Data;
import java.io.Serializable;
/**
 * 结算银行账户
 */
@Data
public class AccountInfo implements Serializable {
    private static final long serialVersionUID=1L;
    /**账户类型*/
    private String bank_account_type;
    /**开户银行*/
    private String account_bank;
    /**开户名称*/
    private String account_name;
    /**开户银行省市编码*/
    private String bank_address_code;
    /**开户银行联行号*/
    private String bank_branch_id;
    /**开户银行全称 (含支行)*/
    private String bank_name;
    /**银行帐号*/
    private String account_number;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/BusinessLicenseInfo.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.shop.util.dto;
/**
 * @ClassName BusinessLicenseInfo
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 12:54
 * @Version 1.0
 */
import lombok.Data;
import java.io.Serializable;
/**
 * 营业执照/登记证书信息
 */
@Data
public class BusinessLicenseInfo implements Serializable {
    private static final long serialVersionUID=1L;
    /**证件扫描件*/
    private String business_license_copy;
    /**证件注册号*/
    private String business_license_number;
    /**商户名称*/
    private String merchant_name;
    /**经营者/法定代表人姓名*/
    private String legal_person;
    /**注册地址*/
    private String company_address;
    /**营业期限*/
    private String business_time;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/CertificateItem.java
New file
@@ -0,0 +1,22 @@
package com.ruoyi.shop.util.dto;
import lombok.Data;
//平台证书item
@Data
public class CertificateItem {
    //加密的平台证书序列号
    private String serial_no;
    //加密的平台证书序列号
    private String effective_time;
    //证书弃用时间
    private String expire_time;
    //证书加密信息
    private EncryptedCertificateItem encrypt_certificate;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/ContactInfo.java
New file
@@ -0,0 +1,31 @@
package com.ruoyi.shop.util.dto;
/**
 * @ClassName ContactInfo
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 12:55
 * @Version 1.0
 */
import lombok.Data;
import java.io.Serializable;
/**
 * 超级管理员信息
 */
@Data
public class ContactInfo implements Serializable {
    private static final long serialVersionUID=1L;
    /**超级管理员类型*/
    private String contact_type;
    /**超级管理员姓名*/
    private String contact_name;
    /**超级管理员身份证件号码*/
    private String contact_id_card_number;
    /**超级管理员手机*/
    private String mobile_phone;
    /**超级管理员邮箱*/
    private String contact_email;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/EncryptedCertificateItem.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.shop.util.dto;
import lombok.Data;
/**
 * @ClassName EncryptedCertificateItem
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 14:12
 * @Version 1.0
 */
@Data
public class EncryptedCertificateItem {
    //加密的平台证书序列号
    private String algorithm;
    //加密的平台证书序列号
    private String nonce;
    //证书弃用时间
    private String associated_data;
    //证书弃用时间
    private String ciphertext;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/IdCardInfo.java
New file
@@ -0,0 +1,31 @@
package com.ruoyi.shop.util.dto;
/**
 * @ClassName IdCardInfo
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 12:54
 * @Version 1.0
 */
import lombok.Data;
import java.io.Serializable;
/**
 * 经营者/法人身份证信息
 */
@Data
public class IdCardInfo implements Serializable {
    private static final long serialVersionUID=1L;
    /**身份证人像面照片*/
    private String id_card_copy;
    /**身份证国徽面照片*/
    private String id_card_national;
    /**身份证姓名*/
    private String id_card_name;
    /**身份证号码*/
    private String id_card_number;
    /**身份证有效期限*/
    private String id_card_valid_time;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/IdDocInfo.java
New file
@@ -0,0 +1,29 @@
package com.ruoyi.shop.util.dto;
/**
 * @ClassName IdDocInfo
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 12:55
 * @Version 1.0
 */
import lombok.Data;
import java.io.Serializable;
/**
 * 经营者/法人其他类型证件信息
 */
@Data
public class IdDocInfo implements Serializable {
    private static final long serialVersionUID=1L;
    /**证件姓名*/
    private String id_doc_name;
    /**证件号码*/
    private String id_doc_number;
    /**证件照片*/
    private String id_doc_copy;
    /**证件结束日期*/
    private String doc_period_end;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/OrganizationCertInfo.java
New file
@@ -0,0 +1,27 @@
package com.ruoyi.shop.util.dto;
/**
 * @ClassName OrganizationCertInfo
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 12:54
 * @Version 1.0
 */
import lombok.Data;
import java.io.Serializable;
/**
 * 组织机构代码证信息
 */
@Data
public class OrganizationCertInfo implements Serializable {
    private static final long serialVersionUID=1L;
    /**组织机构代码证照片*/
    private String organization_copy;
    /**组织机构代码*/
    private String organization_number;
    /**组织机构代码有效期限*/
    private String organization_time;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/PlainCertificateItem.java
New file
@@ -0,0 +1,23 @@
package com.ruoyi.shop.util.dto;
import lombok.Data;
/**
 * @ClassName PlainCertificateItem
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 14:14
 * @Version 1.0
 */
@Data
public class PlainCertificateItem {
    private String serialNo;
    private String effectiveTime;
    private String expireTime;
    private String plainCertificate;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/SalesSceneInfo.java
New file
@@ -0,0 +1,29 @@
package com.ruoyi.shop.util.dto;
/**
 * @ClassName SalesSceneInfo
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 12:56
 * @Version 1.0
 */
import lombok.Data;
import java.io.Serializable;
/**
 * 店铺信息
 */
@Data
public class SalesSceneInfo implements Serializable {
    private static final long serialVersionUID=1L;
    /**店铺名称*/
    private String store_name;
    /**店铺链接*/
    private String store_url;
    /**店铺二维码*/
    private String store_qr_code;
    /**小程序AppID*/
    private String mini_program_sub_appid;
}
ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/dto/SubmitInfo.java
New file
@@ -0,0 +1,51 @@
package com.ruoyi.shop.util.dto;
/**
 * @ClassName SubmitInfo
 * @Description TODO
 * @Author jqs
 * @Date 2023/6/19 12:53
 * @Version 1.0
 */
import lombok.Data;
import java.io.Serializable;
/**
 * 进件信息
 */
@Data
public class SubmitInfo implements Serializable {
    private static final long serialVersionUID=1L;
    /**业务申请编号*/
    private String out_request_no;
    /**主体类型*/
    private String organization_type;
    /**营业执照/登记证书信息*/
    private BusinessLicenseInfo business_license_info;
    /**组织机构代码证信息*/
    private OrganizationCertInfo organization_cert_info;
    /**经营者/法人证件类型*/
    private String id_doc_type;
    /**经营者/法人身份证信息*/
    private IdCardInfo id_card_info;
    /**经营者/法人其他类型证件信息*/
    private IdDocInfo id_doc_info;
    /**是否填写结算银行账户*/
    private Boolean need_account_info;
    /**结算银行账户*/
    private AccountInfo account_info;
    /**超级管理员信息*/
    private ContactInfo contact_info;
    /**店铺信息*/
    private SalesSceneInfo sales_scene_info;
    /**商户简称*/
    private String merchant_shortname;
    /**特殊资质*/
    private String qualifications;
    /**补充材料*/
    private String business_addition_pics;
    /**补充说明*/
    private String business_addition_desc;
}