@ -0,0 +1,258 @@ | |||
<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 :md="6" :sm="12"> | |||
<a-form-item label="用户编号"> | |||
<a-input placeholder="请输入用户编号" v-model="queryParam.userId"></a-input> | |||
</a-form-item> | |||
</a-col> | |||
<a-col :md="6" :sm="12"> | |||
<a-form-item label="交易类型"> | |||
<j-dict-select-tag dict-code="balance_type" placeholder="请选择交易类型" v-model="queryParam.type"></j-dict-select-tag> | |||
</a-form-item> | |||
</a-col> | |||
<a-col :md="6" :sm="12"> | |||
<a-form-item label="状态"> | |||
<j-dict-select-tag dict-code="balance_status" placeholder="请选择状态" v-model="queryParam.status"></j-dict-select-tag> | |||
</a-form-item> | |||
</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('hotel_balance_log')">导出</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>--> | |||
<div v-if="record.status === 0"> | |||
<a @click="handleAudit(record,0)">审核通过</a> | |||
<a-divider type="vertical" /> | |||
<a @click="handleAudit(record,1)">审核不通过</a> | |||
</div> | |||
<div v-if="record.status === 1"> | |||
<a @click="handleWithdrawal(record)">打款</a> | |||
</div> | |||
<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> | |||
<hotel-balance-log-modal ref="modalForm" @ok="modalFormOk"></hotel-balance-log-modal> | |||
</a-card> | |||
</template> | |||
<script> | |||
import '@/assets/less/TableExpand.less' | |||
import { mixinDevice } from '@/utils/mixin' | |||
import { JeecgListMixin } from '@/mixins/JeecgListMixin' | |||
import HotelBalanceLogModal from './modules/HotelBalanceLogModal' | |||
import { httpAction, getAction } from '@/api/manage' | |||
export default { | |||
name: 'HotelBalanceLogList', | |||
mixins:[JeecgListMixin, mixinDevice], | |||
components: { | |||
HotelBalanceLogModal | |||
}, | |||
data () { | |||
return { | |||
description: 'hotel_balance_log管理页面', | |||
// 表头 | |||
columns: [ | |||
{ | |||
title: '#', | |||
dataIndex: '', | |||
key:'rowIndex', | |||
width:60, | |||
align:"center", | |||
customRender:function (t,r,index) { | |||
return parseInt(index)+1; | |||
} | |||
}, | |||
{ | |||
title:'用户编号', | |||
align:"center", | |||
dataIndex: 'userId' | |||
}, | |||
{ | |||
title:'余额', | |||
align:"center", | |||
dataIndex: 'balance' | |||
}, | |||
{ | |||
title:'用户余额', | |||
align:"center", | |||
dataIndex: 'userBalance' | |||
}, | |||
{ | |||
title:'类型', | |||
align:"center", | |||
dataIndex: 'type_dictText' | |||
}, | |||
{ | |||
title:'状态', | |||
align:"center", | |||
dataIndex: 'status_dictText' | |||
}, | |||
{ | |||
title:'是否删除', | |||
align:"center", | |||
dataIndex: 'delFlag_dictText' | |||
}, | |||
{ | |||
title: '操作', | |||
dataIndex: 'action', | |||
align:"center", | |||
fixed:"right", | |||
width:147, | |||
scopedSlots: { customRender: 'action' } | |||
} | |||
], | |||
url: { | |||
list: "/hotelbalancelog/hotelBalanceLog/list2", | |||
delete: "/hotelbalancelog/hotelBalanceLog/delete", | |||
deleteBatch: "/hotelbalancelog/hotelBalanceLog/deleteBatch", | |||
exportXlsUrl: "/hotelbalancelog/hotelBalanceLog/exportXls", | |||
importExcelUrl: "hotelbalancelog/hotelBalanceLog/importExcel", | |||
auditUrl: "/hotelbalancelog/hotelBalanceLog/audit", | |||
withdrawalUrl: "/hotelbalancelog/hotelBalanceLog/withdrawal" | |||
}, | |||
dictOptions:{}, | |||
superFieldList:[], | |||
} | |||
}, | |||
created() { | |||
this.getSuperFieldList(); | |||
}, | |||
computed: { | |||
importExcelUrl: function(){ | |||
return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`; | |||
}, | |||
}, | |||
methods: { | |||
initDictConfig(){ | |||
}, | |||
getSuperFieldList(){ | |||
let fieldList=[]; | |||
fieldList.push({type:'string',value:'userId',text:'用户id',dictCode:''}) | |||
fieldList.push({type:'BigDecimal',value:'balance',text:'余额',dictCode:''}) | |||
// fieldList.push({type:'BigDecimal',value:'userBalance',text:'用户余额',dictCode:''}) | |||
// fieldList.push({type:'int',value:'type',text:'类型 0交易 1提现',dictCode:''}) | |||
// fieldList.push({type:'int',value:'status',text:'状态 0未审核 1已审核未打款 2已打款 3审核未通过',dictCode:''}) | |||
// fieldList.push({type:'int',value:'delFlag',text:'是否删除 0否 1是',dictCode:''}) | |||
this.superFieldList = fieldList | |||
}, | |||
handleAudit(obj,e){ | |||
let param = { | |||
id: obj.id, | |||
audit: e | |||
} | |||
httpAction(this.url.auditUrl,param,'POST').then((res)=>{ | |||
if(res.success){ | |||
this.$message.success(res.message); | |||
this.loadData(); | |||
}else{ | |||
this.$message.warning(res.message); | |||
} | |||
}) | |||
}, | |||
handleWithdrawal(obj){ | |||
let param = { | |||
id: obj.id | |||
} | |||
httpAction(this.url.withdrawalUrl,param,"POST").then((res)=>{ | |||
if(res.success){ | |||
this.$message.success(res.message); | |||
this.loadData(); | |||
}else{ | |||
this.$message.warning(res.message); | |||
} | |||
}) | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped> | |||
@import '~@assets/less/common.less'; | |||
</style> |
@ -0,0 +1,192 @@ | |||
package org.jeecg.common.util; | |||
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.common.util; | |||
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.common.util; | |||
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,164 @@ | |||
package org.jeecg.common.util.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); | |||
} | |||
/** | |||
* 获取支付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,21 @@ | |||
package org.jeecg.modules.hotelbalancelog.req; | |||
import lombok.Data; | |||
/** | |||
* @author java996.icu | |||
* @title: AuditReq | |||
* @projectName hanhai-object-jdbc-20240829 | |||
* @description: TODO | |||
* @date 2025/1/16 22:49 | |||
* @Version V1.0 | |||
*/ | |||
@Data | |||
public class AuditReq { | |||
/**id*/ | |||
private String id; | |||
/**0审核通过 1审核不通过*/ | |||
private Integer audit; | |||
} |
@ -0,0 +1,25 @@ | |||
package org.jeecg.modules.hotelbalancelog.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,43 @@ | |||
package org.jeecg.modules.hotelbalancelog.req; | |||
import com.alibaba.fastjson.annotation.JSONField; | |||
import lombok.Data; | |||
import javax.crypto.Mac; | |||
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 = "transfer_scene_report_infos") | |||
private List<TransferBatchesDetailsRequest> transferDetailList; | |||
} |
@ -0,0 +1,27 @@ | |||
package org.jeecg.modules.hotelbalancelog.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 = "batch_id") | |||
private String batchId; | |||
/**商家批次单号*/ | |||
@JSONField(name = "out_batch_no") | |||
private String outBatchNo; | |||
/**批次创建时间*/ | |||
@JSONField(name = "create_time") | |||
private String createTime; | |||
} |