| @ -0,0 +1,192 @@ | |||
| package org.jeecg.config; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.apache.http.HttpEntity; | |||
| import org.apache.http.HttpResponse; | |||
| import org.apache.http.HttpStatus; | |||
| import org.apache.http.ParseException; | |||
| import org.apache.http.client.ClientProtocolException; | |||
| import org.apache.http.client.methods.CloseableHttpResponse; | |||
| import org.apache.http.client.methods.HttpGet; | |||
| import org.apache.http.client.methods.HttpPost; | |||
| import org.apache.http.entity.StringEntity; | |||
| import org.apache.http.impl.client.CloseableHttpClient; | |||
| import org.apache.http.impl.client.DefaultHttpClient; | |||
| import org.apache.http.impl.client.HttpClients; | |||
| import org.apache.http.util.EntityUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import java.io.IOException; | |||
| import java.net.URLDecoder; | |||
| /** | |||
| * Created by null on 2017/2/23. | |||
| */ | |||
| @Slf4j | |||
| public class HttpRequestUtil { | |||
| private static Logger logger = LoggerFactory.getLogger(HttpRequestUtil.class); //日志记录 | |||
| /** | |||
| * post请求 | |||
| * @param url url地址 | |||
| * @param jsonParam 参数 | |||
| * @return | |||
| */ | |||
| public static String post(String url,String jsonParam){ | |||
| //post请求返回结果 | |||
| DefaultHttpClient httpClient = new DefaultHttpClient(); | |||
| String jsonResult = null; | |||
| HttpPost method = new HttpPost(url); | |||
| try { | |||
| if (null != jsonParam) { | |||
| //解决中文乱码问题 | |||
| StringEntity entity = new StringEntity(jsonParam.toString(), "utf-8"); | |||
| entity.setContentEncoding("UTF-8"); | |||
| entity.setContentType("application/json"); | |||
| method.setEntity(entity); | |||
| } | |||
| HttpResponse result = httpClient.execute(method); | |||
| url = URLDecoder.decode(url, "UTF-8"); | |||
| /**请求发送成功,并得到响应**/ | |||
| if (result.getStatusLine().getStatusCode() == 200) { | |||
| String str = ""; | |||
| try { | |||
| /**读取服务器返回过来的json字符串数据**/ | |||
| str = EntityUtils.toString(result.getEntity()); | |||
| return str; | |||
| /**把json字符串转换成json对象**/ | |||
| // jsonResult = JSONObject.fromObject(str); | |||
| } catch (Exception e) { | |||
| logger.error("post请求提交失败:" + url, e); | |||
| } | |||
| } | |||
| } catch (IOException e) { | |||
| logger.error("post请求提交失败:" + url, e); | |||
| } | |||
| return jsonResult; | |||
| } | |||
| /** | |||
| * 发送get请求 | |||
| * @param url 路径 | |||
| * @return | |||
| */ | |||
| public static String get(String url){ | |||
| String responseString = null; | |||
| try { | |||
| DefaultHttpClient client = new DefaultHttpClient(); | |||
| //发送get请求 | |||
| HttpGet request = new HttpGet(url); | |||
| HttpResponse response = client.execute(request); | |||
| /**请求发送成功,并得到响应**/ | |||
| if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { | |||
| /**读取服务器返回过来的json字符串数据**/ | |||
| return EntityUtils.toString(response.getEntity()); | |||
| /**把json字符串转换成json对象**/ | |||
| } else { | |||
| logger.error("get请求提交失败:" + url); | |||
| } | |||
| } catch (IOException e) { | |||
| logger.error("get请求提交失败:" + url, e); | |||
| } | |||
| return responseString; | |||
| } | |||
| /** | |||
| * 发起批量转账API 批量转账到零钱 | |||
| * | |||
| * @param requestUrl | |||
| * @param requestJson 组合参数 | |||
| * @param wechatPayserialNo 商户证书序列号 | |||
| * @param mchID4M 商户号 | |||
| * @param privatekeypath 商户私钥证书路径 | |||
| * @return | |||
| */ | |||
| public static String postTransBatRequest( | |||
| String requestUrl, | |||
| String requestJson, | |||
| String wechatPayserialNo, | |||
| String wechatPayserialNo2, | |||
| String mchID4M, | |||
| String privatekeypath) { | |||
| CloseableHttpClient httpclient = HttpClients.createDefault(); | |||
| CloseableHttpResponse response = null; | |||
| HttpEntity entity = null; | |||
| try { | |||
| //商户私钥证书 | |||
| HttpPost httpPost = new HttpPost(requestUrl); | |||
| // NOTE: 建议指定charset=utf-8。低于4.4.6版本的HttpCore,不能正确的设置字符集,可能导致签名错误 | |||
| httpPost.addHeader("Content-Type", "application/json"); | |||
| httpPost.addHeader("Accept", "application/json"); | |||
| //"55E551E614BAA5A3EA38AE03849A76D8C7DA735A"); | |||
| httpPost.addHeader("Wechatpay-Serial", wechatPayserialNo); | |||
| //-------------------------核心认证 start----------------------------------------------------------------- | |||
| String strToken = VechatPayV3Util.getToken("POST", | |||
| "/v3/fund-app/mch-transfer/transfer-bills", | |||
| requestJson,mchID4M,wechatPayserialNo2, privatekeypath); | |||
| log.error("微信转账token "+strToken); | |||
| // 添加认证信息 | |||
| httpPost.addHeader("Authorization", | |||
| "WECHATPAY2-SHA256-RSA2048" + " " | |||
| + strToken); | |||
| //---------------------------核心认证 end--------------------------------------------------------------- | |||
| httpPost.setEntity(new StringEntity(requestJson, "UTF-8")); | |||
| //发起转账请求 | |||
| response = httpclient.execute(httpPost); | |||
| entity = response.getEntity();//获取返回的数据 | |||
| log.info("-----getHeaders.Request-ID:"+response.getHeaders("Request-ID")); | |||
| return EntityUtils.toString(entity,"UTF-8"); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } finally { | |||
| // 关闭流 | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * 发送HTTP_GET请求 | |||
| * | |||
| * @see 该方法会自动关闭连接,释放资源 | |||
| * @param reqURL | |||
| * 请求地址(含参数) | |||
| * @param decodeCharset | |||
| * 解码字符集,解析响应数据时用之,其为null时默认采用UTF-8解码 | |||
| * @return 远程主机响应正文 | |||
| */ | |||
| public static String sendGetRequest(String reqURL,String auth,String decodeCharset) { | |||
| long responseLength = 0; // 响应长度 | |||
| String responseContent = null; // 响应内容 | |||
| CloseableHttpClient httpclient = HttpClients.createDefault(); | |||
| HttpGet httpGet = new HttpGet(reqURL); // 创建org.apache.http.client.methods.HttpGet | |||
| httpGet.addHeader("Authorization", auth); | |||
| httpGet.addHeader("Accept", "application/json"); | |||
| httpGet.addHeader("User-Agent", "https://zh.wikipedia.org/wiki/User_agent"); | |||
| try { | |||
| HttpResponse response = httpclient.execute(httpGet); // 执行GET请求 | |||
| HttpEntity entity = response.getEntity(); // 获取响应实体 | |||
| if (null != entity) { | |||
| responseLength = entity.getContentLength(); | |||
| responseContent = EntityUtils.toString(entity, decodeCharset == null ? "UTF-8" : decodeCharset); | |||
| EntityUtils.consume(entity); // Consume response content | |||
| } | |||
| } catch (ClientProtocolException e) { | |||
| System.out.println("该异常通常是协议错误导致,比如构造HttpGet对象时传入的协议不对(将'http'写成'htp')或者服务器端返回的内容不符合HTTP协议要求等,堆栈信息如下"); | |||
| } catch (ParseException e) { | |||
| System.out.println(e.getMessage()); | |||
| } catch (IOException e) { | |||
| System.out.println("该异常通常是网络原因引起的,如HTTP服务器未启动等,堆栈信息如下"); | |||
| } finally { | |||
| httpclient.getConnectionManager().shutdown(); // 关闭连接,释放资源 | |||
| } | |||
| return responseContent; | |||
| } | |||
| } | |||
| @ -0,0 +1,28 @@ | |||
| package org.jeecg.config; | |||
| import java.math.BigDecimal; | |||
| /** | |||
| * Created by 廖师兄 | |||
| * 2017-07-02 13:53 | |||
| */ | |||
| public class MoneyUtil { | |||
| /** | |||
| * 元转分 | |||
| * @param yuan | |||
| * @return | |||
| */ | |||
| public static Integer Yuan2Fen(Double yuan) { | |||
| return new BigDecimal(String.valueOf(yuan)).movePointRight(2).intValue(); | |||
| } | |||
| /** | |||
| * 分转元 | |||
| * @param fen | |||
| * @return | |||
| */ | |||
| public static Double Fen2Yuan(Integer fen) { | |||
| return new BigDecimal(fen).movePointLeft(2).doubleValue(); | |||
| } | |||
| } | |||
| @ -0,0 +1,122 @@ | |||
| package org.jeecg.config; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.apache.commons.codec.binary.Base64; | |||
| import org.springframework.util.StringUtils; | |||
| import java.io.IOException; | |||
| import java.nio.file.Files; | |||
| import java.nio.file.Paths; | |||
| import java.security.KeyFactory; | |||
| import java.security.NoSuchAlgorithmException; | |||
| import java.security.PrivateKey; | |||
| import java.security.Signature; | |||
| import java.security.spec.InvalidKeySpecException; | |||
| import java.security.spec.PKCS8EncodedKeySpec; | |||
| import java.util.Random; | |||
| /** | |||
| * @author java996.icu | |||
| * @title: VechatPayV3Util | |||
| * @projectName chemu | |||
| * @description: TODO | |||
| * @date 2022/12/8 15:00 | |||
| * @Version V1.0 | |||
| */ | |||
| @Slf4j | |||
| public class VechatPayV3Util { | |||
| /** | |||
| * | |||
| * @param method 请求方法 post | |||
| * @param canonicalUrl 请求地址 | |||
| * @param body 请求参数 | |||
| * @param merchantId 这里用的商户号 | |||
| * @param certSerialNo 商户证书序列号 | |||
| * @param keyPath 商户证书地址 | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| public static String getToken( | |||
| String method, | |||
| String canonicalUrl, | |||
| String body, | |||
| String merchantId, | |||
| String certSerialNo, | |||
| String keyPath) throws Exception { | |||
| String signStr = ""; | |||
| //获取32位随机字符串 | |||
| String nonceStr = getRandomString(32); | |||
| //当前系统运行时间 | |||
| long timestamp = System.currentTimeMillis() / 1000; | |||
| if (StringUtils.isEmpty(body)) { | |||
| body = ""; | |||
| } | |||
| //签名操作 | |||
| String message = buildMessage(method, canonicalUrl, timestamp, nonceStr, body); | |||
| //签名操作 | |||
| String signature = sign(message.getBytes("utf-8"), keyPath); | |||
| //组装参数 | |||
| signStr = "mchid=\"" + merchantId + "\",timestamp=\"" + timestamp+ "\",nonce_str=\"" + nonceStr | |||
| + "\",serial_no=\"" + certSerialNo + "\",signature=\"" + signature + "\""; | |||
| return signStr; | |||
| } | |||
| public static String buildMessage(String method, String canonicalUrl, long timestamp, String nonceStr, String body) { | |||
| // String canonicalUrl = url.encodedPath(); | |||
| // if (url.encodedQuery() != null) { | |||
| // canonicalUrl += "?" + url.encodedQuery(); | |||
| // } | |||
| return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n"; | |||
| } | |||
| public static String sign(byte[] message, String keyPath) throws Exception { | |||
| Signature sign = Signature.getInstance("SHA256withRSA"); | |||
| sign.initSign(getPrivateKey(keyPath)); | |||
| sign.update(message); | |||
| return Base64.encodeBase64String(sign.sign()); | |||
| } | |||
| /** | |||
| * 微信支付-前端唤起支付参数-获取商户私钥 | |||
| * | |||
| * @param filename 私钥文件路径 (required) | |||
| * @return 私钥对象 | |||
| */ | |||
| public static PrivateKey getPrivateKey(String filename) throws IOException { | |||
| log.error("签名 证书地址是 "+filename); | |||
| String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8"); | |||
| try { | |||
| String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "") | |||
| .replace("-----END PRIVATE KEY-----", "") | |||
| .replaceAll("\\s+", ""); | |||
| //System.out.println("--------privateKey---------:"+privateKey); | |||
| KeyFactory kf = KeyFactory.getInstance("RSA"); | |||
| return kf.generatePrivate( | |||
| new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey))); | |||
| } catch (NoSuchAlgorithmException e) { | |||
| throw new RuntimeException("当前Java环境不支持RSA", e); | |||
| } catch (InvalidKeySpecException e) { | |||
| throw new RuntimeException("无效的密钥格式"); | |||
| } | |||
| } | |||
| /** | |||
| * 获取随机位数的字符串 | |||
| * @param length | |||
| * @return | |||
| */ | |||
| public static String getRandomString(int length) { | |||
| String base = "abcdefghijklmnopqrstuvwxyz0123456789"; | |||
| Random random = new Random(); | |||
| StringBuffer sb = new StringBuffer(); | |||
| for (int i = 0; i < length; i++) { | |||
| int number = random.nextInt(base.length()); | |||
| sb.append(base.charAt(number)); | |||
| } | |||
| return sb.toString(); | |||
| } | |||
| } | |||
| @ -0,0 +1,207 @@ | |||
| package org.jeecg.config.pay; | |||
| import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; | |||
| import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner; | |||
| import com.wechat.pay.contrib.apache.httpclient.auth.Verifier; | |||
| import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials; | |||
| import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator; | |||
| 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 lombok.Data; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.apache.http.impl.client.CloseableHttpClient; | |||
| import org.springframework.boot.context.properties.ConfigurationProperties; | |||
| import org.springframework.context.annotation.Bean; | |||
| import org.springframework.stereotype.Component; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.nio.charset.StandardCharsets; | |||
| import java.security.GeneralSecurityException; | |||
| import java.security.PrivateKey; | |||
| /** | |||
| * @author java996.icu | |||
| * @title: WeChatPayConfig2 | |||
| * @projectName chemu | |||
| * @description: TODO | |||
| * @date 2022/12/7 16:36 | |||
| * @Version V1.0 | |||
| */ | |||
| @Component | |||
| @Data | |||
| @Slf4j | |||
| @ConfigurationProperties(prefix = "wxpay") | |||
| public class WeChatPayConfig2 { | |||
| /** | |||
| * 应用编号 | |||
| */ | |||
| private String appId; | |||
| /** | |||
| * 商户号 | |||
| */ | |||
| private String mchId; | |||
| /** | |||
| * 服务商商户号 | |||
| */ | |||
| private String slMchId; | |||
| /** | |||
| * APIv2密钥 | |||
| */ | |||
| private String apiKey; | |||
| /** | |||
| * APIv3密钥 | |||
| */ | |||
| private String apiV3Key; | |||
| /** | |||
| * 支付通知回调地址 | |||
| */ | |||
| private String notifyUrl; | |||
| /** | |||
| * 退款回调地址 | |||
| */ | |||
| private String refundNotifyUrl; | |||
| /** | |||
| * API 证书中的 key.pem | |||
| */ | |||
| private String keyPemPath; | |||
| /** | |||
| * 商户序列号 | |||
| */ | |||
| private String serialNo; | |||
| /** | |||
| * 微信支付V3-url前缀 | |||
| */ | |||
| private String baseUrl; | |||
| /** | |||
| * 获取商户的私钥文件 | |||
| * @param keyPemPath | |||
| * @return | |||
| */ | |||
| public PrivateKey getPrivateKey(String keyPemPath){ | |||
| InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(keyPemPath); | |||
| if(inputStream==null){ | |||
| throw new RuntimeException("私钥文件不存在"); | |||
| } | |||
| return PemUtil.loadPrivateKey(inputStream); | |||
| } | |||
| // | |||
| // /** | |||
| // * 获取证书管理器实例 | |||
| // * @return | |||
| // */ | |||
| // @Bean | |||
| // public Verifier getVerifier() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException { | |||
| // | |||
| // log.info("获取证书管理器实例"); | |||
| // | |||
| // //获取商户私钥 | |||
| // PrivateKey privateKey = getPrivateKey(keyPemPath); | |||
| // | |||
| // //私钥签名对象 | |||
| // PrivateKeySigner privateKeySigner = new PrivateKeySigner(serialNo, privateKey); | |||
| // | |||
| // //身份认证对象 | |||
| // WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner); | |||
| // | |||
| // // 使用定时更新的签名验证器,不需要传入证书 | |||
| // CertificatesManager certificatesManager = CertificatesManager.getInstance(); | |||
| // certificatesManager.putMerchant(mchId,wechatPay2Credentials,apiV3Key.getBytes(StandardCharsets.UTF_8)); | |||
| // | |||
| // return certificatesManager.getVerifier(mchId); | |||
| // } | |||
| @Bean | |||
| public Verifier getVerifier() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException { | |||
| log.info("开始获取微信支付证书管理器实例"); | |||
| // 验证关键参数 | |||
| log.debug("商户号: {}", mchId); | |||
| log.debug("证书序列号: {}", serialNo); | |||
| log.debug("APIv3密钥长度: {}", apiV3Key.length()); // 不记录具体密钥内容 | |||
| // 获取商户私钥 | |||
| PrivateKey privateKey = getPrivateKey(keyPemPath); | |||
| if (privateKey == null) { | |||
| throw new IllegalArgumentException("无法从路径加载私钥: " + keyPemPath); | |||
| } | |||
| // 创建签名器 | |||
| PrivateKeySigner privateKeySigner = new PrivateKeySigner(serialNo, privateKey); | |||
| WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner); | |||
| try { | |||
| CertificatesManager certificatesManager = CertificatesManager.getInstance(); | |||
| // 显式设置域名(如果库需要) | |||
| // certificatesManager.setDomain("api.mch.weixin.qq.com"); | |||
| // 添加商户信息 | |||
| certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8)); | |||
| // 获取验证器 | |||
| Verifier verifier = certificatesManager.getVerifier(mchId); | |||
| log.info("成功获取证书管理器实例"); | |||
| return verifier; | |||
| } catch (HttpCodeException e) { | |||
| log.error("微信支付API返回错误: "+ e); | |||
| throw e; | |||
| } catch (Exception e) { | |||
| log.error("获取证书管理器实例时发生错误", e); | |||
| throw e; | |||
| } | |||
| } | |||
| /** | |||
| * 获取支付http请求对象 | |||
| * @param verifier | |||
| * @return | |||
| */ | |||
| @Bean(name = "wxPayClient") | |||
| public CloseableHttpClient getWxPayClient(Verifier verifier) { | |||
| //获取商户私钥 | |||
| PrivateKey privateKey = getPrivateKey(keyPemPath); | |||
| WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() | |||
| .withMerchant(mchId, serialNo, privateKey) | |||
| .withValidator(new WechatPay2Validator(verifier)); | |||
| // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新 | |||
| return builder.build(); | |||
| } | |||
| /** | |||
| * 获取HttpClient,无需进行应答签名验证,跳过验签的流程 | |||
| */ | |||
| @Bean(name = "wxPayNoSignClient") | |||
| public CloseableHttpClient getWxPayNoSignClient(){ | |||
| //获取商户私钥 | |||
| PrivateKey privateKey = getPrivateKey(keyPemPath); | |||
| //用于构造HttpClient | |||
| WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() | |||
| //设置商户信息 | |||
| .withMerchant(mchId, serialNo, privateKey) | |||
| //无需进行签名验证、通过withValidator((response) -> true)实现 | |||
| .withValidator((response) -> true); | |||
| // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新 | |||
| return builder.build(); | |||
| } | |||
| } | |||
| @ -0,0 +1,253 @@ | |||
| <template> | |||
| <a-card :bordered="false"> | |||
| <!-- 查询区域 --> | |||
| <div class="table-page-search-wrapper"> | |||
| <a-form layout="inline" @keyup.enter.native="searchQuery"> | |||
| <a-row :gutter="24"> | |||
| <a-col :xl="6" :lg="7" :md="8" :sm="24"> | |||
| <a-form-item label="状态"> | |||
| <j-dict-select-tag placeholder="请选择状态" v-model="queryParam.state" dictCode="money_pay_state"/> | |||
| </a-form-item> | |||
| </a-col> | |||
| <a-col :xl="6" :lg="7" :md="8" :sm="24"> | |||
| <a-form-item label="类型"> | |||
| <j-dict-select-tag placeholder="请选择类型" v-model="queryParam.type" dictCode="money_pay"/> | |||
| </a-form-item> | |||
| </a-col> | |||
| <a-col :xl="6" :lg="7" :md="8" :sm="24"> | |||
| <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons"> | |||
| <a-button type="primary" @click="searchQuery" icon="search">查询</a-button> | |||
| <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button> | |||
| <a @click="handleToggleSearch" style="margin-left: 8px"> | |||
| {{ toggleSearchStatus ? '收起' : '展开' }} | |||
| <a-icon :type="toggleSearchStatus ? 'up' : 'down'"/> | |||
| </a> | |||
| </span> | |||
| </a-col> | |||
| </a-row> | |||
| </a-form> | |||
| </div> | |||
| <!-- 查询区域-END --> | |||
| <!-- 操作按钮区域 --> | |||
| <div class="table-operator"> | |||
| <a-button @click="handleAdd" type="primary" icon="plus">新增</a-button> | |||
| <a-button type="primary" icon="download" @click="handleExportXls('佣金流水')">导出</a-button> | |||
| <a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel"> | |||
| <a-button type="primary" icon="import">导入</a-button> | |||
| </a-upload> | |||
| <!-- 高级查询区域 --> | |||
| <j-super-query :fieldList="superFieldList" ref="superQueryModal" @handleSuperQuery="handleSuperQuery"></j-super-query> | |||
| <a-dropdown v-if="selectedRowKeys.length > 0"> | |||
| <a-menu slot="overlay"> | |||
| <a-menu-item key="1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item> | |||
| </a-menu> | |||
| <a-button style="margin-left: 8px"> 批量操作 <a-icon type="down" /></a-button> | |||
| </a-dropdown> | |||
| </div> | |||
| <!-- table区域-begin --> | |||
| <div> | |||
| <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;"> | |||
| <i class="anticon anticon-info-circle ant-alert-icon"></i> 已选择 <a style="font-weight: 600">{{ selectedRowKeys.length }}</a>项 | |||
| <a style="margin-left: 24px" @click="onClearSelected">清空</a> | |||
| </div> | |||
| <a-table | |||
| ref="table" | |||
| size="middle" | |||
| :scroll="{x:true}" | |||
| bordered | |||
| rowKey="id" | |||
| :columns="columns" | |||
| :dataSource="dataSource" | |||
| :pagination="ipagination" | |||
| :loading="loading" | |||
| :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}" | |||
| class="j-table-force-nowrap" | |||
| @change="handleTableChange"> | |||
| <template slot="htmlSlot" slot-scope="text"> | |||
| <div v-html="text"></div> | |||
| </template> | |||
| <template slot="imgSlot" slot-scope="text,record"> | |||
| <span v-if="!text" style="font-size: 12px;font-style: italic;">无图片</span> | |||
| <img v-else :src="getImgView(text)" :preview="record.id" height="25px" alt="" style="max-width:80px;font-size: 12px;font-style: italic;"/> | |||
| </template> | |||
| <template slot="fileSlot" slot-scope="text"> | |||
| <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span> | |||
| <a-button | |||
| v-else | |||
| :ghost="true" | |||
| type="primary" | |||
| icon="download" | |||
| size="small" | |||
| @click="downloadFile(text)"> | |||
| 下载 | |||
| </a-button> | |||
| </template> | |||
| <span slot="action" slot-scope="text, record"> | |||
| <a @click="handleEdit(record)">编辑</a> | |||
| <a-divider type="vertical" /> | |||
| <a-dropdown> | |||
| <a class="ant-dropdown-link">更多 <a-icon type="down" /></a> | |||
| <a-menu slot="overlay"> | |||
| <a-menu-item> | |||
| <a @click="handleDetail(record)">详情</a> | |||
| </a-menu-item> | |||
| <a-menu-item> | |||
| <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)"> | |||
| <a>删除</a> | |||
| </a-popconfirm> | |||
| </a-menu-item> | |||
| </a-menu> | |||
| </a-dropdown> | |||
| </span> | |||
| </a-table> | |||
| </div> | |||
| <city-money-log-modal ref="modalForm" @ok="modalFormOk"></city-money-log-modal> | |||
| </a-card> | |||
| </template> | |||
| <script> | |||
| import '@/assets/less/TableExpand.less' | |||
| import { mixinDevice } from '@/utils/mixin' | |||
| import { JeecgListMixin } from '@/mixins/JeecgListMixin' | |||
| import CityMoneyLogModal from './modules/CityMoneyLogModal' | |||
| import {filterMultiDictText} from '@/components/dict/JDictSelectUtil' | |||
| export default { | |||
| name: 'CityMoneyLogList', | |||
| mixins:[JeecgListMixin, mixinDevice], | |||
| components: { | |||
| CityMoneyLogModal | |||
| }, | |||
| data () { | |||
| return { | |||
| description: '佣金流水管理页面', | |||
| // 表头 | |||
| columns: [ | |||
| { | |||
| title: '#', | |||
| dataIndex: '', | |||
| key:'rowIndex', | |||
| width:60, | |||
| align:"center", | |||
| customRender:function (t,r,index) { | |||
| return parseInt(index)+1; | |||
| } | |||
| }, | |||
| { | |||
| title:'创建日期', | |||
| align:"center", | |||
| sorter: true, | |||
| dataIndex: 'createTime' | |||
| }, | |||
| { | |||
| title:'用户', | |||
| align:"center", | |||
| dataIndex: 'userId_dictText' | |||
| }, | |||
| { | |||
| title:'提现者姓名', | |||
| align:"center", | |||
| dataIndex: 'name' | |||
| }, | |||
| { | |||
| title:'流水备注', | |||
| align:"center", | |||
| dataIndex: 'title' | |||
| }, | |||
| { | |||
| title:'金额', | |||
| align:"center", | |||
| dataIndex: 'price' | |||
| }, | |||
| { | |||
| title:'状态', | |||
| align:"center", | |||
| dataIndex: 'state_dictText' | |||
| }, | |||
| { | |||
| title:'到账时间', | |||
| align:"center", | |||
| dataIndex: 'successtime' | |||
| }, | |||
| { | |||
| title:'类型', | |||
| align:"center", | |||
| dataIndex: 'type_dictText' | |||
| }, | |||
| { | |||
| title:'回调', | |||
| align:"center", | |||
| dataIndex: 'packageInfo' | |||
| }, | |||
| { | |||
| title:'标识', | |||
| align:"center", | |||
| dataIndex: 'outBatchNo' | |||
| }, | |||
| { | |||
| title:'内容', | |||
| align:"center", | |||
| dataIndex: 'batchId' | |||
| }, | |||
| { | |||
| title: '操作', | |||
| dataIndex: 'action', | |||
| align:"center", | |||
| fixed:"right", | |||
| width:147, | |||
| scopedSlots: { customRender: 'action' } | |||
| } | |||
| ], | |||
| url: { | |||
| list: "/cityMoneyLog/cityMoneyLog/list", | |||
| delete: "/cityMoneyLog/cityMoneyLog/delete", | |||
| deleteBatch: "/cityMoneyLog/cityMoneyLog/deleteBatch", | |||
| exportXlsUrl: "/cityMoneyLog/cityMoneyLog/exportXls", | |||
| importExcelUrl: "cityMoneyLog/cityMoneyLog/importExcel", | |||
| }, | |||
| dictOptions:{}, | |||
| superFieldList:[], | |||
| } | |||
| }, | |||
| created() { | |||
| this.getSuperFieldList(); | |||
| }, | |||
| computed: { | |||
| importExcelUrl: function(){ | |||
| return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`; | |||
| }, | |||
| }, | |||
| methods: { | |||
| initDictConfig(){ | |||
| }, | |||
| getSuperFieldList(){ | |||
| let fieldList=[]; | |||
| fieldList.push({type:'datetime',value:'createTime',text:'创建日期'}) | |||
| fieldList.push({type:'sel_search',value:'userId',text:'用户',dictTable:"han_hai_member", dictText:'nick_name', dictCode:'id'}) | |||
| fieldList.push({type:'string',value:'name',text:'提现者姓名',dictCode:''}) | |||
| fieldList.push({type:'string',value:'title',text:'流水备注',dictCode:''}) | |||
| fieldList.push({type:'BigDecimal',value:'price',text:'金额',dictCode:''}) | |||
| fieldList.push({type:'int',value:'state',text:'状态',dictCode:'money_pay_state'}) | |||
| fieldList.push({type:'datetime',value:'successtime',text:'到账时间'}) | |||
| fieldList.push({type:'int',value:'type',text:'类型',dictCode:'money_pay'}) | |||
| fieldList.push({type:'Text',value:'packageInfo',text:'回调',dictCode:''}) | |||
| fieldList.push({type:'Text',value:'outBatchNo',text:'标识',dictCode:''}) | |||
| fieldList.push({type:'Text',value:'batchId',text:'内容',dictCode:''}) | |||
| this.superFieldList = fieldList | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped> | |||
| @import '~@assets/less/common.less'; | |||
| </style> | |||
| @ -0,0 +1,149 @@ | |||
| <template> | |||
| <a-spin :spinning="confirmLoading"> | |||
| <j-form-container :disabled="formDisabled"> | |||
| <a-form-model ref="form" :model="model" :rules="validatorRules" slot="detail"> | |||
| <a-row> | |||
| <a-col :span="24"> | |||
| <a-form-model-item label="用户" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="userId"> | |||
| <j-search-select-tag v-model="model.userId" dict="han_hai_member,nick_name,id" /> | |||
| </a-form-model-item> | |||
| </a-col> | |||
| <a-col :span="24"> | |||
| <a-form-model-item label="提现者姓名" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="name"> | |||
| <a-input v-model="model.name" placeholder="请输入提现者姓名" ></a-input> | |||
| </a-form-model-item> | |||
| </a-col> | |||
| <a-col :span="24"> | |||
| <a-form-model-item label="流水备注" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="title"> | |||
| <a-input v-model="model.title" placeholder="请输入流水备注" ></a-input> | |||
| </a-form-model-item> | |||
| </a-col> | |||
| <a-col :span="24"> | |||
| <a-form-model-item label="金额" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="price"> | |||
| <a-input-number v-model="model.price" placeholder="请输入金额" style="width: 100%" /> | |||
| </a-form-model-item> | |||
| </a-col> | |||
| <a-col :span="24"> | |||
| <a-form-model-item label="状态" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="state"> | |||
| <j-dict-select-tag type="list" v-model="model.state" dictCode="money_pay_state" placeholder="请选择状态" /> | |||
| </a-form-model-item> | |||
| </a-col> | |||
| <a-col :span="24"> | |||
| <a-form-model-item label="到账时间" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="successtime"> | |||
| <j-date placeholder="请选择到账时间" v-model="model.successtime" :show-time="true" date-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" /> | |||
| </a-form-model-item> | |||
| </a-col> | |||
| <a-col :span="24"> | |||
| <a-form-model-item label="类型" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="type"> | |||
| <j-dict-select-tag type="list" v-model="model.type" dictCode="money_pay" placeholder="请选择类型" /> | |||
| </a-form-model-item> | |||
| </a-col> | |||
| <a-col :span="24"> | |||
| <a-form-model-item label="回调" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="packageInfo"> | |||
| <a-input v-model="model.packageInfo" placeholder="请输入回调" ></a-input> | |||
| </a-form-model-item> | |||
| </a-col> | |||
| <a-col :span="24"> | |||
| <a-form-model-item label="标识" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="outBatchNo"> | |||
| <a-input v-model="model.outBatchNo" placeholder="请输入标识" ></a-input> | |||
| </a-form-model-item> | |||
| </a-col> | |||
| <a-col :span="24"> | |||
| <a-form-model-item label="内容" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="batchId"> | |||
| <a-input v-model="model.batchId" placeholder="请输入内容" ></a-input> | |||
| </a-form-model-item> | |||
| </a-col> | |||
| </a-row> | |||
| </a-form-model> | |||
| </j-form-container> | |||
| </a-spin> | |||
| </template> | |||
| <script> | |||
| import { httpAction, getAction } from '@/api/manage' | |||
| import { validateDuplicateValue } from '@/utils/util' | |||
| export default { | |||
| name: 'CityMoneyLogForm', | |||
| components: { | |||
| }, | |||
| props: { | |||
| //表单禁用 | |||
| disabled: { | |||
| type: Boolean, | |||
| default: false, | |||
| required: false | |||
| } | |||
| }, | |||
| data () { | |||
| return { | |||
| model:{ | |||
| }, | |||
| labelCol: { | |||
| xs: { span: 24 }, | |||
| sm: { span: 5 }, | |||
| }, | |||
| wrapperCol: { | |||
| xs: { span: 24 }, | |||
| sm: { span: 16 }, | |||
| }, | |||
| confirmLoading: false, | |||
| validatorRules: { | |||
| }, | |||
| url: { | |||
| add: "/cityMoneyLog/cityMoneyLog/add", | |||
| edit: "/cityMoneyLog/cityMoneyLog/edit", | |||
| queryById: "/cityMoneyLog/cityMoneyLog/queryById" | |||
| } | |||
| } | |||
| }, | |||
| computed: { | |||
| formDisabled(){ | |||
| return this.disabled | |||
| }, | |||
| }, | |||
| created () { | |||
| //备份model原始值 | |||
| this.modelDefault = JSON.parse(JSON.stringify(this.model)); | |||
| }, | |||
| methods: { | |||
| add () { | |||
| this.edit(this.modelDefault); | |||
| }, | |||
| edit (record) { | |||
| this.model = Object.assign({}, record); | |||
| this.visible = true; | |||
| }, | |||
| submitForm () { | |||
| const that = this; | |||
| // 触发表单验证 | |||
| this.$refs.form.validate(valid => { | |||
| if (valid) { | |||
| that.confirmLoading = true; | |||
| let httpurl = ''; | |||
| let method = ''; | |||
| if(!this.model.id){ | |||
| httpurl+=this.url.add; | |||
| method = 'post'; | |||
| }else{ | |||
| httpurl+=this.url.edit; | |||
| method = 'put'; | |||
| } | |||
| httpAction(httpurl,this.model,method).then((res)=>{ | |||
| if(res.success){ | |||
| that.$message.success(res.message); | |||
| that.$emit('ok'); | |||
| }else{ | |||
| that.$message.warning(res.message); | |||
| } | |||
| }).finally(() => { | |||
| that.confirmLoading = false; | |||
| }) | |||
| } | |||
| }) | |||
| }, | |||
| } | |||
| } | |||
| </script> | |||
| @ -0,0 +1,84 @@ | |||
| <template> | |||
| <a-drawer | |||
| :title="title" | |||
| :width="width" | |||
| placement="right" | |||
| :closable="false" | |||
| @close="close" | |||
| destroyOnClose | |||
| :visible="visible"> | |||
| <city-money-log-form ref="realForm" @ok="submitCallback" :disabled="disableSubmit" normal></city-money-log-form> | |||
| <div class="drawer-footer"> | |||
| <a-button @click="handleCancel" style="margin-bottom: 0;">关闭</a-button> | |||
| <a-button v-if="!disableSubmit" @click="handleOk" type="primary" style="margin-bottom: 0;">提交</a-button> | |||
| </div> | |||
| </a-drawer> | |||
| </template> | |||
| <script> | |||
| import CityMoneyLogForm from './CityMoneyLogForm' | |||
| export default { | |||
| name: 'CityMoneyLogModal', | |||
| components: { | |||
| CityMoneyLogForm | |||
| }, | |||
| data () { | |||
| return { | |||
| title:"操作", | |||
| width:800, | |||
| visible: false, | |||
| disableSubmit: false | |||
| } | |||
| }, | |||
| methods: { | |||
| add () { | |||
| this.visible=true | |||
| this.$nextTick(()=>{ | |||
| this.$refs.realForm.add(); | |||
| }) | |||
| }, | |||
| edit (record) { | |||
| this.visible=true | |||
| this.$nextTick(()=>{ | |||
| this.$refs.realForm.edit(record); | |||
| }); | |||
| }, | |||
| close () { | |||
| this.$emit('close'); | |||
| this.visible = false; | |||
| }, | |||
| submitCallback(){ | |||
| this.$emit('ok'); | |||
| this.visible = false; | |||
| }, | |||
| handleOk () { | |||
| this.$refs.realForm.submitForm(); | |||
| }, | |||
| handleCancel () { | |||
| this.close() | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| <style lang="less" scoped> | |||
| /** Button按钮间距 */ | |||
| .ant-btn { | |||
| margin-left: 30px; | |||
| margin-bottom: 30px; | |||
| float: right; | |||
| } | |||
| .drawer-footer{ | |||
| position: absolute; | |||
| bottom: -8px; | |||
| width: 100%; | |||
| border-top: 1px solid #e8e8e8; | |||
| padding: 10px 16px; | |||
| text-align: right; | |||
| left: 0; | |||
| background: #fff; | |||
| border-radius: 0 0 2px 2px; | |||
| } | |||
| </style> | |||
| @ -0,0 +1,60 @@ | |||
| <template> | |||
| <j-modal | |||
| :title="title" | |||
| :width="width" | |||
| :visible="visible" | |||
| switchFullscreen | |||
| @ok="handleOk" | |||
| :okButtonProps="{ class:{'jee-hidden': disableSubmit} }" | |||
| @cancel="handleCancel" | |||
| cancelText="关闭"> | |||
| <city-money-log-form ref="realForm" @ok="submitCallback" :disabled="disableSubmit"></city-money-log-form> | |||
| </j-modal> | |||
| </template> | |||
| <script> | |||
| import CityMoneyLogForm from './CityMoneyLogForm' | |||
| export default { | |||
| name: 'CityMoneyLogModal', | |||
| components: { | |||
| CityMoneyLogForm | |||
| }, | |||
| data () { | |||
| return { | |||
| title:'', | |||
| width:800, | |||
| visible: false, | |||
| disableSubmit: false | |||
| } | |||
| }, | |||
| methods: { | |||
| add () { | |||
| this.visible=true | |||
| this.$nextTick(()=>{ | |||
| this.$refs.realForm.add(); | |||
| }) | |||
| }, | |||
| edit (record) { | |||
| this.visible=true | |||
| this.$nextTick(()=>{ | |||
| this.$refs.realForm.edit(record); | |||
| }) | |||
| }, | |||
| close () { | |||
| this.$emit('close'); | |||
| this.visible = false; | |||
| }, | |||
| handleOk () { | |||
| this.$refs.realForm.submitForm(); | |||
| }, | |||
| submitCallback(){ | |||
| this.$emit('ok'); | |||
| this.visible = false; | |||
| }, | |||
| handleCancel () { | |||
| this.close() | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| @ -0,0 +1,25 @@ | |||
| package org.jeecg.modules.cityMoneyLog.req; | |||
| import com.alibaba.fastjson.annotation.JSONField; | |||
| import lombok.Data; | |||
| /** | |||
| * @author java996.icu | |||
| * @title: TransferBatchesDetailsRequest | |||
| * @projectName merchant | |||
| * @description: TODO | |||
| * @date 2022/10/10 15:45 | |||
| * @Version V1.0 | |||
| */ | |||
| @Data | |||
| public class TransferBatchesDetailsRequest { | |||
| /**转账场景为1000-现金营销,需填入活动名称、奖励说明*/ | |||
| @JSONField(name = "info_type") | |||
| private String infoType; | |||
| /**信息类型为活动名称,请在信息内容描述用户参与活动的名称,如新会员有礼 | |||
| 信息类型为奖励说明,请在信息内容描述用户因为什么奖励获取这笔资金,如注册会员抽奖一等奖*/ | |||
| @JSONField(name = "info_content") | |||
| private String infoContent; | |||
| } | |||
| @ -0,0 +1,45 @@ | |||
| package org.jeecg.modules.cityMoneyLog.req; | |||
| import com.alibaba.fastjson.annotation.JSONField; | |||
| import lombok.Data; | |||
| import java.util.List; | |||
| /** | |||
| * @author java996.icu | |||
| * @title: TransferBatchesRequest | |||
| * @projectName merchant | |||
| * @description: TODO | |||
| * @date 2022/10/10 15:32 | |||
| * @Version V1.0 | |||
| */ | |||
| @Data | |||
| public class TransferBatchesRequest { | |||
| /**直连商户的appid*/ | |||
| private String appid; | |||
| /**商家批次单号*/ | |||
| @JSONField(name = "out_bill_no") | |||
| private String outBillNo; | |||
| /**批次备注*/ | |||
| @JSONField(name = "transfer_remark") | |||
| private String transferRemark; | |||
| /**转账总金额*/ | |||
| @JSONField(name = "transfer_amount") | |||
| private Integer transferAmount; | |||
| /**转账场景*/ | |||
| @JSONField(name = "transfer_scene_id") | |||
| private String transferSceneId; | |||
| @JSONField(name = "openid") | |||
| private String openid; | |||
| /**真实姓名*/ | |||
| @JSONField(name = "user_name") | |||
| private String userName; | |||
| /**回调地址*/ | |||
| @JSONField(name = "notify_url") | |||
| private String notifyUrl; | |||
| /**发起批量转账的明细列表,最多三千笔*/ | |||
| @JSONField(name = "transfer_scene_report_infos") | |||
| private List<TransferBatchesDetailsRequest> transferDetailList; | |||
| } | |||
| @ -0,0 +1,32 @@ | |||
| package org.jeecg.modules.cityMoneyLog.resp; | |||
| import com.alibaba.fastjson.annotation.JSONField; | |||
| import lombok.Data; | |||
| @Data | |||
| public class TransferBatchNotifyResourceResp { | |||
| /** | |||
| * 加密算法类型 | |||
| * 对开启结果数据进行加密的加密算法,目前只支持AEAD_AES_256_GCM | |||
| */ | |||
| @JSONField(name = "algorithm") | |||
| private String algorithm; | |||
| /** | |||
| * 数据密文 | |||
| * Base64编码后的商家转账结果数据密文 | |||
| */ | |||
| @JSONField(name = "ciphertext") | |||
| private String ciphertext; | |||
| /**附加数据*/ | |||
| @JSONField(name = "associated_data") | |||
| private String associatedData; | |||
| /**原始回调类型,为mch_payment*/ | |||
| @JSONField(name = "original_type") | |||
| private String originalType; | |||
| /**加密使用的随机串*/ | |||
| @JSONField(name = "nonce") | |||
| private String nonce; | |||
| } | |||
| @ -0,0 +1,51 @@ | |||
| package org.jeecg.modules.cityMoneyLog.resp; | |||
| import com.alibaba.fastjson.annotation.JSONField; | |||
| import lombok.Data; | |||
| @Data | |||
| public class TransferBatchResourceDataResp { | |||
| /**微信批次单号*/ | |||
| @JSONField(name = "out_bill_no") | |||
| private String outBillNo; | |||
| /**商家批次单号*/ | |||
| @JSONField(name = "transfer_bill_no") | |||
| private String transferBillNo; | |||
| /** | |||
| * 商家转账订单状态 | |||
| * ACCEPTED: 转账已受理 | |||
| * | |||
| * PROCESSING: 转账处理中,转账结果尚未明确,如一直处于此状态,建议检查账户余额是否足够 | |||
| * | |||
| * WAIT_USER_CONFIRM: 待收款用户确认,可拉起微信收款确认页面进行收款确认 | |||
| * | |||
| * TRANSFERING: 转账结果尚未明确,可拉起微信收款确认页面再次重试确认收款 | |||
| * | |||
| * SUCCESS: 转账成功 | |||
| * | |||
| * FAIL: 转账失败 | |||
| * | |||
| * CANCELING: 商户撤销请求受理成功,该笔转账正在撤销中 | |||
| * | |||
| * CANCELLED: 转账撤销完成 | |||
| * */ | |||
| @JSONField(name = "state") | |||
| private String state; | |||
| /**失败原因*/ | |||
| @JSONField(name = "fail_reason") | |||
| private String failReason; | |||
| /**商户号*/ | |||
| @JSONField(name = "mch_id") | |||
| private String mchId; | |||
| /**转账总金额,单位为“分”*/ | |||
| @JSONField(name = "transfer_amount") | |||
| private Integer transferAmount; | |||
| @JSONField(name = "openid") | |||
| private String openid; | |||
| @JSONField(name = "create_time") | |||
| private String createTime; | |||
| @JSONField(name = "update_time") | |||
| private String updateTime; | |||
| } | |||
| @ -0,0 +1,31 @@ | |||
| package org.jeecg.modules.cityMoneyLog.resp; | |||
| import com.alibaba.fastjson.annotation.JSONField; | |||
| import lombok.Data; | |||
| @Data | |||
| public class TransferBatchesNotifyResp { | |||
| private String id; | |||
| @JSONField(name = "create_time") | |||
| private String createTime; | |||
| /** | |||
| * 通知类型 | |||
| * 通知的类型,商家转账通知的类型为MCHTRANSFER.BILL.FINISHED | |||
| * */ | |||
| @JSONField(name = "event_type") | |||
| private String eventType; | |||
| /** | |||
| * 通知类型 | |||
| * 通知的资源数据类型,商家转账通知为encrypt-resource | |||
| * */ | |||
| @JSONField(name = "resource_type") | |||
| private String resourceType; | |||
| /**通知数据*/ | |||
| @JSONField(name = "resource") | |||
| private TransferBatchNotifyResourceResp resource; | |||
| /**回调摘要*/ | |||
| @JSONField(name = "summary") | |||
| private String summary; | |||
| } | |||
| @ -0,0 +1,53 @@ | |||
| package org.jeecg.modules.cityMoneyLog.resp; | |||
| import com.alibaba.fastjson.annotation.JSONField; | |||
| import lombok.Data; | |||
| /** | |||
| * @author java996.icu | |||
| * @title: TransferBatchesResp | |||
| * @projectName merchant | |||
| * @description: TODO | |||
| * @date 2022/12/13 14:29 | |||
| * @Version V1.0 | |||
| */ | |||
| @Data | |||
| public class TransferBatchesResp { | |||
| /**微信批次单号*/ | |||
| @JSONField(name = "out_bill_no") | |||
| private String outBillNo; | |||
| /**商家批次单号*/ | |||
| @JSONField(name = "transfer_bill_no") | |||
| private String transferBillNo; | |||
| /**批次创建时间*/ | |||
| @JSONField(name = "create_time") | |||
| private String createTime; | |||
| /**商家转账订单状态 | |||
| * ACCEPTED: 转账已受理 | |||
| * | |||
| * PROCESSING: 转账处理中,转账结果尚未明确,如一直处于此状态,建议检查账户余额是否足够 | |||
| * | |||
| * WAIT_USER_CONFIRM: 待收款用户确认,可拉起微信收款确认页面进行收款确认 | |||
| * | |||
| * TRANSFERING: 转账结果尚未明确,可拉起微信收款确认页面再次重试确认收款 | |||
| * | |||
| * SUCCESS: 转账成功 | |||
| * | |||
| * FAIL: 转账失败 | |||
| * | |||
| * CANCELING: 商户撤销请求受理成功,该笔转账正在撤销中 | |||
| * | |||
| * CANCELLED: 转账撤销完成 | |||
| * */ | |||
| @JSONField(name = "state") | |||
| private String state; | |||
| /**失败原因*/ | |||
| @JSONField(name = "fail_reason") | |||
| private String failReason; | |||
| /**跳转领取页面的package信息*/ | |||
| @JSONField(name = "package_info") | |||
| private String packageInfo; | |||
| } | |||
| @ -1,14 +1,13 @@ | |||
| package org.jeecg.modules.api.bean; | |||
| import io.swagger.annotations.ApiModelProperty; | |||
| import lombok.Data; | |||
| @Data | |||
| public class WxQrCodeVo { | |||
| /**图片地址*/ | |||
| @ApiModelProperty(value = "图片地址") | |||
| private String url; | |||
| @ApiModelProperty(value = "加油站名称") | |||
| private String name; | |||
| } | |||
| @ -0,0 +1,25 @@ | |||
| -----BEGIN CERTIFICATE----- | |||
| MIIELjCCAxagAwIBAgIUJG7Xen+IKln9eZk9Cf3SupqGj/4wDQYJKoZIhvcNAQEL | |||
| BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT | |||
| FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg | |||
| Q0EwHhcNMjUwNTMwMTA1NzMzWhcNMzAwNTI5MTA1NzMzWjCBhzETMBEGA1UEAwwK | |||
| MTY3MzUxNjE3NjEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTMwMQYDVQQL | |||
| DCrmuZbljZfngJrmtbfpu47mmI7kv6Hmga/np5HmioDmnInpmZDlhazlj7gxCzAJ | |||
| BgNVBAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQAD | |||
| ggEPADCCAQoCggEBALAOE2yiLLnpUg7/iVTEZeORO0W7AUdL3zskg4I0aT997pbw | |||
| CpeSV+6K83+QlcWKnpdG0WkS9xqRgIuA/WZPYJVJG1iDUo+ndYNofx1UpZxfzvQq | |||
| zSZTB1GnxIHHJQTvWF7CpxWXNKRcFmVRx5TKorL3BqYrqD/FgUBKIJNWUgEsVqJs | |||
| EW8dv2iBYZdIHB51rc3wWsr2X28SGmBVOSCbG2gEkmgQr7yS7KpzE2rfl2PfsnWs | |||
| KQBSX9KMToXRmEIwHVAGmWXV5RlzBACk1vOSz3Cp+GgKFcOPqrFg2pJOXwSyVUL5 | |||
| WUc6t6xDqnkfg9bO5za1v4Q8S7fxK5NhMIChXVsCAwEAAaOBuTCBtjAJBgNVHRME | |||
| AjAAMAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDov | |||
| L2V2Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUw | |||
| REJDMDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJF | |||
| MTJCMjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IB | |||
| AQBjK72tgf+Fn+9qxWhiiwyow6MtIE3arbPDhB/3WfKjHp7AA6eqdGPMrUvfufYv | |||
| evM2I7ZJWsk6IiJMJlweyBalqn84Aq3raurEoL5Y+jPjXqLHTrcUfyIiGpbF8Auy | |||
| bpW1pl1w2GqRnA17Ep/o0Na+9KOX+iBvv1Lm/LJnwlxJaSaOU2PFRS5QT8NRuV2/ | |||
| 82/FFnFfM+rDk98YjLVtob45tBKIkBeRJZ+Zsz5a7SBYnR2eVW6YgOS0fFtBI9Q/ | |||
| 6fuMHLbuAH9/xubPG6K4bHwVyNGHCeDp4GjfuOX/DSw49qVGiy5GxYGCczgNta+L | |||
| hlDUuptj8HhkGjrExeXfZQsR | |||
| -----END CERTIFICATE----- | |||
| @ -0,0 +1,9 @@ | |||
| -----BEGIN PUBLIC KEY----- | |||
| MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxK2wqQooeGKkAQchZdwK | |||
| U3qFiPSyf83l7USGGAdEhb78iq0Dvi7RfgdHuqSVBv0fnSQJicNO+s10ofi9waxl | |||
| SCPnuO8t9MeOuS4IpZ54VWY/9pJ5A2Z0x49L0djoFWStFCpKzsg2fWBvc/7kYVFr | |||
| nq/jFyRho8/GZtxL9RLZjWLyfnpe+erxSNFEnQLoW6LC4D5L0w9oiHboHmN9Igzc | |||
| uB6pIuMwccImX1xPeu/jx2QMYrxAW/2bW3e6z4ojQWqhRtFq55INnXLV8VXE7rwI | |||
| 1L0RB4R39JOsKroVE/g7SiHRbGEem2BbNaAFhjCMuZzpWSCFQkTlxLJMxxTt9j0S | |||
| +wIDAQAB | |||
| -----END PUBLIC KEY----- | |||
| @ -1,7 +1,7 @@ | |||
| pay.mchId=1673516176 | |||
| pay.appId=wxa4d29e67e8a58d38 | |||
| pay.mchKey=GVIP1008611ABCDEFGGGGJKHLOSJFLGK | |||
| pay.keyPath= | |||
| pay.keyPath=classpath:apiclient_cert.pem | |||
| pay.notifyUrl=http://h5.xzaiyp.top/api/order/payNotify | |||
| pay.notifyUrlDev=http://h5.xzaiyp.top/api/order/payNotify | |||
| pay.notifyVipUrl=http://h5.xzaiyp.top/api/order/vipPayNotify | |||
| @ -0,0 +1,18 @@ | |||
| 欢迎使用微信支付! | |||
| 附件中的三份文件(证书pkcs12格式、证书pem格式、证书密钥pem格式),为接口中强制要求时需携带的证书文件。 | |||
| 证书属于敏感信息,请妥善保管不要泄露和被他人复制。 | |||
| 不同开发语言下的证书格式不同,以下为说明指引: | |||
| 证书pkcs12格式(apiclient_cert.p12) | |||
| 包含了私钥信息的证书文件,为p12(pfx)格式,由微信支付签发给您用来标识和界定您的身份 | |||
| 部分安全性要求较高的API需要使用该证书来确认您的调用身份 | |||
| windows上可以直接双击导入系统,导入过程中会提示输入证书密码,证书密码默认为您的商户号(如:1900006031) | |||
| 证书pem格式(apiclient_cert.pem) | |||
| 从apiclient_cert.p12中导出证书部分的文件,为pem格式,请妥善保管不要泄漏和被他人复制 | |||
| 部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供 | |||
| 您也可以使用openssl命令来自己导出:openssl pkcs12 -clcerts -nokeys -in apiclient_cert.p12 -out apiclient_cert.pem | |||
| 证书密钥pem格式(apiclient_key.pem) | |||
| 从apiclient_cert.p12中导出密钥部分的文件,为pem格式 | |||
| 部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供 | |||
| 您也可以使用openssl命令来自己导出:openssl pkcs12 -nocerts -in apiclient_cert.p12 -out apiclient_key.pem | |||
| 备注说明: | |||
| 由于绝大部分操作系统已内置了微信支付服务器证书的根CA证书, 2018年3月6日后, 不再提供CA证书文件(rootca.pem)下载 | |||