@ -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; | package org.jeecg.modules.api.bean; | ||||
import io.swagger.annotations.ApiModelProperty; | |||||
import lombok.Data; | import lombok.Data; | ||||
@Data | @Data | ||||
public class WxQrCodeVo { | public class WxQrCodeVo { | ||||
/**图片地址*/ | |||||
@ApiModelProperty(value = "图片地址") | |||||
private String url; | private String url; | ||||
@ApiModelProperty(value = "加油站名称") | |||||
private String name; | 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.mchId=1673516176 | ||||
pay.appId=wxa4d29e67e8a58d38 | pay.appId=wxa4d29e67e8a58d38 | ||||
pay.mchKey=GVIP1008611ABCDEFGGGGJKHLOSJFLGK | pay.mchKey=GVIP1008611ABCDEFGGGGJKHLOSJFLGK | ||||
pay.keyPath= | |||||
pay.keyPath=classpath:apiclient_cert.pem | |||||
pay.notifyUrl=http://h5.xzaiyp.top/api/order/payNotify | pay.notifyUrl=http://h5.xzaiyp.top/api/order/payNotify | ||||
pay.notifyUrlDev=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 | 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)下载 |