You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
852 lines
39 KiB
852 lines
39 KiB
package com.example.demo.serviceImpl.cash;
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.alibaba.fastjson.JSONArray;
|
|
import com.alibaba.fastjson.JSONObject;
|
|
import com.example.demo.domain.DTO.*;
|
|
import com.example.demo.domain.vo.cash.BankVO;
|
|
import com.example.demo.domain.vo.cash.CashCollection;
|
|
import com.example.demo.domain.vo.coin.Result;
|
|
import com.example.demo.mapper.cash.CashCollectionMapper;
|
|
import com.example.demo.service.cash.BankService;
|
|
import com.stripe.Stripe;
|
|
import com.stripe.exception.StripeException;
|
|
import com.stripe.model.BalanceTransaction;
|
|
import com.stripe.model.Charge;
|
|
import com.stripe.model.ChargeCollection;
|
|
import com.stripe.param.ChargeListParams;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.http.*;
|
|
import org.springframework.scheduling.annotation.Scheduled;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.util.LinkedMultiValueMap;
|
|
import org.springframework.util.MultiValueMap;
|
|
import org.springframework.web.client.RestTemplate;
|
|
|
|
import javax.crypto.Mac;
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
import java.math.BigDecimal;
|
|
import java.net.http.HttpResponse;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.security.MessageDigest;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.text.SimpleDateFormat;
|
|
import java.time.LocalDate;
|
|
import java.time.ZoneId;
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* @program: gold-java
|
|
* @ClassName BankServiceImpl
|
|
* @description:
|
|
* @author: Double
|
|
* @create: 2025−11-21 10:46
|
|
* @Version 1.0
|
|
**/
|
|
|
|
@Service
|
|
@Slf4j
|
|
public class BankServiceImpl implements BankService {
|
|
|
|
@Autowired
|
|
private CashCollectionMapper cashCollectionMapper;
|
|
|
|
// 第三方API地址
|
|
private static final String API_URL = "https://gateway.pa-sys.com/v1.1/reconciliation/519e26b2-8145-418c-b3e7-c1e88e52b946/settlement";
|
|
// 签名密钥
|
|
private static final String SECRET = "8987d1b8-1d82-4b15-af06-828d0b12076f";
|
|
|
|
// 注入RestTemplate用于HTTP请求(需在Spring配置类中定义)
|
|
private final RestTemplate restTemplate;
|
|
|
|
public BankServiceImpl(RestTemplate restTemplate) {
|
|
this.restTemplate = restTemplate;
|
|
}
|
|
|
|
//payment银行接口(单个)
|
|
@Override
|
|
public Result getPayment(BankDTO bankDTO) {
|
|
try {
|
|
// 1. 准备参数
|
|
String settlementDate = bankDTO.getTime(); // 从BankDTO对象获取time作为settlement_date
|
|
String network = "FPS"; // 固定值
|
|
// 2. 生成签名
|
|
Map<String, String> params = new TreeMap<>(); // 按key升序排序
|
|
params.put("settlement_date", settlementDate);
|
|
params.put("network", network);
|
|
|
|
String signSource = buildQueryString(params) + SECRET;
|
|
String sign = sha512(signSource);
|
|
|
|
// 3. 构建form-data请求参数
|
|
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
|
|
formData.add("settlement_date", settlementDate);
|
|
formData.add("network", network);
|
|
formData.add("sign", sign);
|
|
|
|
// 4. 发送HTTP POST请求(优化:显式设置multipart/form-data的字符集)
|
|
HttpHeaders headers = new HttpHeaders();
|
|
// 补充charset=UTF-8,避免部分服务器对编码敏感
|
|
headers.setContentType(new MediaType("multipart", "form-data", StandardCharsets.UTF_8));
|
|
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
|
|
|
|
// 调用第三方API(使用配置好SSL协议的RestTemplate)
|
|
ResponseEntity<String> response = restTemplate.exchange(
|
|
API_URL,
|
|
HttpMethod.POST,
|
|
requestEntity,
|
|
String.class
|
|
);
|
|
|
|
if (response.getStatusCode().is2xxSuccessful()) {
|
|
String responseBody = response.getBody();
|
|
log.info("第三方API响应: {}", responseBody);
|
|
// 解析JSON获取payload.transactions数组
|
|
JSONObject jsonObject = JSON.parseObject(responseBody);
|
|
JSONArray transactions = jsonObject.getJSONObject("payload").getJSONArray("transactions");
|
|
// 创建BankDTO并设置paymentDTOList
|
|
BankVO bankVO = new BankVO();
|
|
List<PaymentDTO> paymentDTOList;
|
|
try {
|
|
paymentDTOList = transactions.toJavaList(PaymentDTO.class);
|
|
} catch (Exception e) {
|
|
log.error("解析JSON响应时发生错误: {}", e.getMessage(), e);
|
|
return Result.error("payment当天无数据请切换日期");
|
|
}
|
|
bankVO.setPaymentDTOList(paymentDTOList);
|
|
// 获取订单号
|
|
String orderNo = bankDTO.getOrderNo();
|
|
|
|
// 如果订单号不为空,则进行匹配查找
|
|
if (orderNo != null && !orderNo.isEmpty()) {
|
|
boolean found = false;
|
|
for (PaymentDTO paymentDTO : paymentDTOList) {
|
|
if (orderNo.equals(paymentDTO.getMerchant_reference())) {
|
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
|
|
paymentDTO.setTime(sdf.parse(bankDTO.getTime()));
|
|
paymentDTO.setOrder_amount(paymentDTO.getOrder_amount().multiply(BigDecimal.valueOf(100)));
|
|
paymentDTO.setCharge(paymentDTO.getCharge().multiply(BigDecimal.valueOf(100)));
|
|
paymentDTO.setNet_amount(paymentDTO.getNet_amount().multiply(BigDecimal.valueOf(100)));
|
|
paymentDTO.setCurrency("港币HKD");
|
|
bankVO.setPaymentDTO(paymentDTO);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
// 如果没有找到匹配的订单号,返回提示信息
|
|
if (!found) {
|
|
log.info("当前日期 {} 该订单号 {} 未查到", settlementDate, orderNo);
|
|
// 可以根据业务需求进行相应处理,比如抛出异常或设置特定标识
|
|
return Result.error("payment当前日期 " + settlementDate + " 该订单号 " + orderNo + " 未查到");
|
|
}
|
|
}
|
|
|
|
CashCollection cashCollection = cashCollectionMapper.selectByGoldCoinOrderCode(orderNo);
|
|
if (cashCollection == null) {
|
|
return Result.error("金币系统当前日期 " + settlementDate + " 该订单号 " + orderNo + " 未查到");
|
|
} else {
|
|
cashCollectionMapper.updateByGoldCoinOrderCodeByPayment(bankVO.getPaymentDTO());
|
|
}
|
|
return Result.success(bankVO);
|
|
} else {
|
|
throw new RuntimeException("API请求失败,状态码: " + response.getStatusCodeValue());
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
log.error("payment银行接口处理失败", e);
|
|
throw new RuntimeException("处理失败: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
//payment银行接口(批量)
|
|
@Override
|
|
public Result paymentAuto(BankDTO bankDTO) {
|
|
try {
|
|
// 1. 准备参数
|
|
String settlementDate = bankDTO.getTime(); // 从BankDTO对象获取time作为settlement_date
|
|
String network = "FPS"; // 固定值
|
|
// 2. 生成签名
|
|
Map<String, String> params = new TreeMap<>(); // 按key升序排序
|
|
params.put("settlement_date", settlementDate);
|
|
params.put("network", network);
|
|
|
|
String signSource = buildQueryString(params) + SECRET;
|
|
String sign = sha512(signSource);
|
|
|
|
// 3. 构建form-data请求参数
|
|
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
|
|
formData.add("settlement_date", settlementDate);
|
|
formData.add("network", network);
|
|
formData.add("sign", sign);
|
|
|
|
// 4. 发送HTTP POST请求(优化:显式设置multipart/form-data的字符集)
|
|
HttpHeaders headers = new HttpHeaders();
|
|
// 补充charset=UTF-8,避免部分服务器对编码敏感
|
|
headers.setContentType(new MediaType("multipart", "form-data", StandardCharsets.UTF_8));
|
|
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
|
|
|
|
// 调用第三方API(使用配置好SSL协议的RestTemplate)
|
|
ResponseEntity<String> response = restTemplate.exchange(
|
|
API_URL,
|
|
HttpMethod.POST,
|
|
requestEntity,
|
|
String.class
|
|
);
|
|
|
|
if (response.getStatusCode().is2xxSuccessful()) {
|
|
String responseBody = response.getBody();
|
|
log.info("第三方API响应: {}", responseBody);
|
|
|
|
// 解析JSON获取payload.transactions数组
|
|
JSONObject jsonObject = JSON.parseObject(responseBody);
|
|
JSONArray transactions = jsonObject.getJSONObject("payload").getJSONArray("transactions");
|
|
|
|
// 创建BankVO并设置paymentDTOList
|
|
BankVO bankVO = new BankVO();
|
|
List<PaymentDTO> paymentDTOList;
|
|
try {
|
|
paymentDTOList = transactions.toJavaList(PaymentDTO.class);
|
|
} catch (Exception e) {
|
|
log.error("解析JSON响应时发生错误: {}", e.getMessage(), e);
|
|
return Result.error("payment当天无数据请切换日期");
|
|
}
|
|
bankVO.setPaymentDTOList(paymentDTOList);
|
|
// 收集处理信息
|
|
List<String> messages = new ArrayList<>();
|
|
// 对paymentDTOList中的每个元素进行处理
|
|
List<PaymentDTO> processedPayments = new ArrayList<>();
|
|
for (PaymentDTO paymentDTO : paymentDTOList) {
|
|
try {
|
|
|
|
// 格式化时间
|
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
|
|
paymentDTO.setTime(sdf.parse(settlementDate));
|
|
// 金额转换(假设原始数据是元为单位,需要转为分为单位)
|
|
paymentDTO.setOrder_amount(paymentDTO.getOrder_amount().multiply(BigDecimal.valueOf(100)));
|
|
paymentDTO.setCharge(paymentDTO.getCharge().multiply(BigDecimal.valueOf(100)));
|
|
paymentDTO.setNet_amount(paymentDTO.getNet_amount().multiply(BigDecimal.valueOf(100)));
|
|
// 设置货币代码
|
|
paymentDTO.setCurrency("港币HKD");
|
|
|
|
// 获取订单号
|
|
String orderNo = paymentDTO.getMerchant_reference();
|
|
|
|
List<String> orderNoList = cashCollectionMapper.selectStripeList();
|
|
// 检查当前订单号是否在列表中
|
|
if (orderNoList.contains(orderNo)) {
|
|
cashCollectionMapper.updateByGoldCoinOrderCodeByPayment(paymentDTO);
|
|
processedPayments.add(paymentDTO);
|
|
log.info("成功处理订单: {}", orderNo);
|
|
messages.add("成功处理订单: " + orderNo);
|
|
}
|
|
else {
|
|
log.warn("金币系统中未找到订单: {}", orderNo);
|
|
messages.add("金币系统中未找到订单: " + orderNo);
|
|
}
|
|
} catch (Exception e) {
|
|
messages.add("处理单个支付记录时发生错误: " + paymentDTO.getMerchant_reference() + ",错误信息: " + e.getMessage());
|
|
log.error("处理单个支付记录时发生错误: {}", paymentDTO.getMerchant_reference(), e);
|
|
}
|
|
}
|
|
|
|
// 设置处理成功的支付列表
|
|
bankVO.setPaymentDTOList(processedPayments);
|
|
bankVO.setMessage(messages);
|
|
|
|
return Result.success(bankVO);
|
|
} else {
|
|
throw new RuntimeException("API请求失败,状态码: " + response.getStatusCodeValue());
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
log.error("payment银行接口处理失败", e);
|
|
throw new RuntimeException("处理失败: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
//stripe银行接口(批量)
|
|
@Override
|
|
public Result stripeAuto(BankDTO bankDTO) {
|
|
try {
|
|
// 设置Stripe API密钥
|
|
Stripe.apiKey = "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";
|
|
if (bankDTO.getSum() <= 0) {
|
|
return Result.error("最大条数不能小于等于0");
|
|
}
|
|
// 收集处理信息
|
|
List<String> messages = new ArrayList<>();
|
|
// 从Stripe获取最近的收费记录(最多200条)
|
|
List<Charge> allCharges = new ArrayList<>();
|
|
String startingAfter = null;
|
|
int totalLimit = bankDTO.getSum(); // 目标获取条数
|
|
int pageSize = 100; // 单次最大获取条数
|
|
|
|
do {
|
|
// 计算当前页需查询的条数(最后一页可能不足100条)
|
|
int currentPageSize = Math.min(pageSize, totalLimit - allCharges.size());
|
|
if (currentPageSize <= 0) {
|
|
break; // 已获取足够条数,停止
|
|
}
|
|
Long startTime = LocalDate.parse(bankDTO.getStartTime(), DateTimeFormatter.ofPattern("yyyyMMdd")).atStartOfDay(ZoneId.of("Asia/Shanghai")).toEpochSecond();
|
|
Long endTime = LocalDate.parse(bankDTO.getEndTime(), DateTimeFormatter.ofPattern("yyyyMMdd")).atStartOfDay(ZoneId.of("Asia/Shanghai")).toEpochSecond();
|
|
ChargeListParams.Created createdCondition = ChargeListParams.Created.builder()
|
|
.setGte(startTime) // 大于等于开始时间
|
|
.setLt(endTime) // 小于结束时间
|
|
.build();
|
|
ChargeListParams params = ChargeListParams.builder()
|
|
.setLimit((long) currentPageSize)
|
|
.setStartingAfter(startingAfter)
|
|
.setCreated(createdCondition) // 加入时间筛选
|
|
.build();
|
|
|
|
try {
|
|
// 执行分页查询
|
|
ChargeCollection charges = Charge.list(params);
|
|
List<Charge> currentPageData = charges.getData();
|
|
allCharges.addAll(currentPageData);
|
|
|
|
// 更新分页游标:若有下一页且未达200条,继续
|
|
boolean hasMore = charges.getHasMore() && allCharges.size() < totalLimit;
|
|
startingAfter = hasMore ? currentPageData.get(currentPageData.size() - 1).getId() : null;
|
|
|
|
} catch (StripeException e) {
|
|
log.error("Stripe 分页查询失败:" + e.getMessage());
|
|
break; // 异常时停止查询,返回已获取的数据
|
|
}
|
|
|
|
} while (startingAfter != null);
|
|
|
|
// 创建StripeDTO列表用于存储所有处理后的数据
|
|
List<StripeDTO> stripeDTOList = new ArrayList<>();
|
|
|
|
// 处理每一条Stripe数据
|
|
for (Charge charge : allCharges) {
|
|
try {
|
|
// 获取charge对应的余额交易ID
|
|
String balanceTransactionId = charge.getBalanceTransaction();
|
|
|
|
// 通过余额交易ID获取详细信息
|
|
BalanceTransaction balanceTransaction = BalanceTransaction.retrieve(balanceTransactionId);
|
|
|
|
// 创建StripeDTO对象并填充所需数据点
|
|
StripeDTO stripeDTO = new StripeDTO();
|
|
|
|
// 从metadata中获取订单号
|
|
if (charge.getMetadata() != null) {
|
|
stripeDTO.setOrderNo(charge.getMetadata().get("order_no"));
|
|
}
|
|
|
|
// 设置余额交易ID
|
|
stripeDTO.setBalanceTransaction(charge.getBalanceTransaction());
|
|
|
|
// 设置付款币种和金额(来自charge)
|
|
stripeDTO.setCurrency(charge.getCurrency().toUpperCase());
|
|
stripeDTO.setAmount(String.valueOf(balanceTransaction.getAmount()));
|
|
|
|
// 设置收款币种(来自charge)
|
|
stripeDTO.setChargeCurrency(charge.getCurrency().toUpperCase());
|
|
|
|
// 设置到账金额和手续费(来自balanceTransaction)
|
|
stripeDTO.setNet(String.valueOf(balanceTransaction.getNet()));
|
|
stripeDTO.setFee(String.valueOf(balanceTransaction.getFee()));
|
|
|
|
// 设置到账币种(来自balanceTransaction)
|
|
stripeDTO.setCurrency(balanceTransaction.getCurrency().toUpperCase());
|
|
|
|
// 设置available_on日期
|
|
if (balanceTransaction.getAvailableOn() != null) {
|
|
long availableOnInSeconds = balanceTransaction.getAvailableOn();
|
|
// 将Unix时间戳转换为Date对象
|
|
Date availableOnDate = new Date(availableOnInSeconds * 1000L);
|
|
stripeDTO.setAvailableOn(availableOnDate);
|
|
}
|
|
|
|
// 添加到列表中
|
|
stripeDTOList.add(stripeDTO);
|
|
|
|
// 如果订单号存在,且在selectStripeList返回的列表中,则更新数据库中的记录
|
|
if (stripeDTO.getOrderNo() != null && !stripeDTO.getOrderNo().isEmpty()) {
|
|
// 获取需要处理的订单号列表
|
|
List<String> orderNoList = cashCollectionMapper.selectStripeList();
|
|
// 检查当前订单号是否在列表中
|
|
if (orderNoList.contains(stripeDTO.getOrderNo())) {
|
|
cashCollectionMapper.updateByGoldCoinOrderCodeByStripe(stripeDTO);
|
|
}
|
|
}
|
|
messages.add("成功处理订单: " + stripeDTO.getOrderNo());
|
|
} catch (Exception e) {
|
|
log.error("处理Stripe数据失败,chargeId: " + charge.getId(), e);
|
|
// 继续处理其他数据,不中断整个流程
|
|
}
|
|
}
|
|
|
|
// 创建响应VO对象
|
|
BankVO bankVO = new BankVO();
|
|
bankVO.setStripeDTOList(stripeDTOList);
|
|
bankVO.setMessage(messages);
|
|
|
|
return Result.success(bankVO);
|
|
} catch (Exception e) {
|
|
log.error("stripe银行接口处理失败", e);
|
|
throw new RuntimeException("处理失败: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
|
|
//stripe银行接口(单个)
|
|
@Override
|
|
public Result getStripe(BankDTO bankDTO) throws StripeException {
|
|
try {
|
|
// 设置Stripe API密钥
|
|
Stripe.apiKey = "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";
|
|
// 方式一:通过订单号查找最近数据
|
|
String orderNo = bankDTO.getOrderNo();
|
|
|
|
if (bankDTO.getSum() <= 0) {
|
|
return Result.error("最大条数不能小于等于0");
|
|
}
|
|
|
|
// 从Stripe获取最近的收费记录(最多200条)
|
|
List<Charge> allCharges = new ArrayList<>();
|
|
String startingAfter = null;
|
|
int totalLimit = bankDTO.getSum(); // 目标获取条数
|
|
int pageSize = 100; // 单次最大获取条数
|
|
|
|
do {
|
|
// 计算当前页需查询的条数(最后一页可能不足100条)
|
|
int currentPageSize = Math.min(pageSize, totalLimit - allCharges.size());
|
|
if (currentPageSize <= 0) {
|
|
break; // 已获取足够条数,停止
|
|
}
|
|
Long startTime = LocalDate.parse(bankDTO.getStartTime(), DateTimeFormatter.ofPattern("yyyyMMdd")).atStartOfDay(ZoneId.of("Asia/Shanghai")).toEpochSecond();
|
|
Long endTime = LocalDate.parse(bankDTO.getEndTime(), DateTimeFormatter.ofPattern("yyyyMMdd")).atStartOfDay(ZoneId.of("Asia/Shanghai")).toEpochSecond();
|
|
ChargeListParams.Created createdCondition = ChargeListParams.Created.builder()
|
|
.setGte(startTime) // 大于等于开始时间
|
|
.setLt(endTime) // 小于结束时间
|
|
.build();
|
|
ChargeListParams params = ChargeListParams.builder()
|
|
.setLimit((long) currentPageSize)
|
|
.setStartingAfter(startingAfter)
|
|
.setCreated(createdCondition) // 加入时间筛选
|
|
.build();
|
|
|
|
try {
|
|
// 执行分页查询
|
|
ChargeCollection charges = Charge.list(params);
|
|
List<Charge> currentPageData = charges.getData();
|
|
allCharges.addAll(currentPageData);
|
|
|
|
// 更新分页游标:若有下一页且未达200条,继续
|
|
boolean hasMore = charges.getHasMore() && allCharges.size() < totalLimit;
|
|
startingAfter = hasMore ? currentPageData.get(currentPageData.size() - 1).getId() : null;
|
|
|
|
} catch (StripeException e) {
|
|
log.error("Stripe 分页查询失败:" + e.getMessage());
|
|
break; // 异常时停止查询,返回已获取的数据
|
|
}
|
|
|
|
} while (startingAfter != null);
|
|
|
|
// 在获取的所有记录中查找匹配订单号的记录
|
|
Charge matchedCharge = null;
|
|
System.out.println(allCharges);
|
|
for (Charge charge : allCharges) {
|
|
// 从metadata中获取订单号进行匹配
|
|
if (charge.getMetadata() != null) {
|
|
String chargeOrderNo = charge.getMetadata().get("order_no");
|
|
if (chargeOrderNo != null && orderNo.equals(chargeOrderNo)) {
|
|
matchedCharge = charge;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 如果未找到匹配的订单,返回错误信息
|
|
if (matchedCharge == null) {
|
|
return Result.error("未找到订单号 " + orderNo + " 的支付记录");
|
|
}
|
|
|
|
// 获取匹配到的charge对应的余额交易ID
|
|
String balanceTransactionId = matchedCharge.getBalanceTransaction();
|
|
|
|
// 通过余额交易ID获取详细信息
|
|
BalanceTransaction balanceTransaction = BalanceTransaction.retrieve(balanceTransactionId);
|
|
|
|
// 创建StripeDTO对象并填充所需数据点
|
|
StripeDTO stripeDTO = new StripeDTO();
|
|
// 设置订单号
|
|
stripeDTO.setOrderNo(matchedCharge.getMetadata().get("order_no"));
|
|
// 设置余额交易ID
|
|
stripeDTO.setBalanceTransaction(matchedCharge.getBalanceTransaction());
|
|
|
|
// 设置付款币种和金额(来自charge和来自balanceTransaction)
|
|
stripeDTO.setCurrency(matchedCharge.getCurrency().toUpperCase());
|
|
stripeDTO.setAmount(String.valueOf(balanceTransaction.getAmount()));
|
|
|
|
// 设置收款币种(来自charge)
|
|
stripeDTO.setChargeCurrency(matchedCharge.getCurrency().toUpperCase());
|
|
|
|
// 设置到账金额和手续费(来自balanceTransaction)
|
|
stripeDTO.setNet(String.valueOf(balanceTransaction.getNet()));
|
|
stripeDTO.setFee(String.valueOf(balanceTransaction.getFee()));
|
|
|
|
// 设置到账币种(来自balanceTransaction)
|
|
stripeDTO.setCurrency(balanceTransaction.getCurrency().toUpperCase());
|
|
|
|
// 设置available_on日期
|
|
if (balanceTransaction.getAvailableOn() != null) {
|
|
long availableOnInSeconds = balanceTransaction.getAvailableOn();
|
|
// 将Unix时间戳转换为Date对象
|
|
Date availableOnDate = new Date(availableOnInSeconds * 1000L);
|
|
stripeDTO.setAvailableOn(availableOnDate);
|
|
}
|
|
|
|
// 创建响应VO对象
|
|
BankVO bankVO = new BankVO();
|
|
bankVO.setStripeDTO(stripeDTO);
|
|
CashCollection cashCollection = cashCollectionMapper.selectByGoldCoinOrderCode(orderNo);
|
|
if (cashCollection == null) {
|
|
return Result.error("金币系统当前日期 " + " 该订单号 " + orderNo + " 未查到");
|
|
} else {
|
|
cashCollectionMapper.updateByGoldCoinOrderCodeByStripe(bankVO.getStripeDTO());
|
|
}
|
|
return Result.success(bankVO);
|
|
} catch (Exception e) {
|
|
log.error("stripe银行接口处理失败", e);
|
|
throw new RuntimeException("处理失败: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
//firstdata银行接口(批量)
|
|
@Override
|
|
public Result firstdataAuto(BankDTO bankDTO) {
|
|
// 获取需要处理的订单号列表
|
|
List<String> orderNoList = cashCollectionMapper.selectFirstdataList();
|
|
|
|
// 存储处理结果的列表
|
|
List<Result> results = new ArrayList<>();
|
|
|
|
// 对每个订单执行getFirstdata方法
|
|
for (String orderNo : orderNoList) {
|
|
// 创建一个新的BankDTO实例,设置订单号
|
|
BankDTO dto = new BankDTO();
|
|
dto.setOrderNo(orderNo);
|
|
|
|
// 调用getFirstdata方法处理单个订单
|
|
Result result = getFirstdata(dto);
|
|
results.add(result);
|
|
}
|
|
|
|
// 返回处理结果列表
|
|
return Result.success(results);
|
|
}
|
|
|
|
//firstdata银行接口(单个)
|
|
@Override
|
|
public Result getFirstdata(BankDTO bankDTO) {
|
|
try {
|
|
CashCollection cashCollection = cashCollectionMapper.selectByBankCode(bankDTO.getOrderNo());
|
|
if (cashCollection == null) {
|
|
return Result.error("金币系统当前日期 " + " 该银行订单号 " + bankDTO.getOrderNo() + " 未查到");
|
|
}
|
|
// 获取签名参数
|
|
FirstdataRequestDTO firstdataRequestDTO = generatePaymentAsiaSignature();
|
|
|
|
// 构建请求URL,使用bankDTO中的orderNo
|
|
String orderNo = bankDTO.getOrderNo();
|
|
String url = "https://prod.api.firstdata.com/gateway/v2/payments/" + orderNo + "?storeId=4530056594";
|
|
|
|
// 使用RestTemplate发送GET请求
|
|
HttpHeaders headers = new HttpHeaders();
|
|
headers.set("accept", "application/json");
|
|
headers.set("Client-Request-Id", String.valueOf(firstdataRequestDTO.getClientRequestId()));
|
|
headers.set("Api-Key", firstdataRequestDTO.getKey());
|
|
headers.set("Timestamp", String.valueOf(firstdataRequestDTO.getTime()));
|
|
headers.set("Message-Signature", firstdataRequestDTO.getHmacBase64());
|
|
headers.set("User-Agent", "Apifox/1.0.0 (https://apifox.com)");
|
|
headers.set("Content-Type", "application/json");
|
|
headers.set("Host", "prod.api.firstdata.com");
|
|
headers.set("Connection", "keep-alive");
|
|
|
|
HttpEntity<String> entity = new HttpEntity<>(headers);
|
|
|
|
ResponseEntity<String> response = restTemplate.exchange(
|
|
url,
|
|
HttpMethod.GET,
|
|
entity,
|
|
String.class
|
|
);
|
|
|
|
// 解析响应数据
|
|
// API返回的是对象格式,需要先解析为JSONObject
|
|
JSONObject jsonObject = JSON.parseObject(response.getBody());
|
|
|
|
// 创建BankVO对象并设置数据
|
|
BankVO bankVO = new BankVO();
|
|
List<String> message = new ArrayList<>();
|
|
|
|
// 检查jsonObject是否为空或者是否包含错误信息
|
|
if (jsonObject != null && !jsonObject.isEmpty()) {
|
|
// 提取需要的字段
|
|
String country = jsonObject.getString("country");
|
|
String orderId = jsonObject.getString("orderId");
|
|
|
|
// 提取currency和total
|
|
JSONObject transactionAmount = jsonObject.getJSONObject("transactionAmount");
|
|
String currency = transactionAmount != null ? transactionAmount.getString("currency") : null;
|
|
Integer total = transactionAmount != null ? transactionAmount.getInteger("total") : null;
|
|
|
|
// 创建FirstdataDTO对象并存储数据
|
|
FirstdataDTO firstdataDTO = new FirstdataDTO();
|
|
firstdataDTO.setCountry(country);
|
|
firstdataDTO.setOrderId(orderId);
|
|
firstdataDTO.setCurrency(currency);
|
|
firstdataDTO.setTotal(total);
|
|
|
|
// 根据要求设置amount为永久金币×100
|
|
Integer amount = cashCollection.getPermanentGold();
|
|
firstdataDTO.setAmount(amount);
|
|
|
|
// 根据国家计算fee
|
|
double feeValue;
|
|
if ("Singapore".equals(country)) {
|
|
// 新加坡:amount的值×百分之2.8加上20保留整数四舍五入
|
|
feeValue = Math.round(amount * 0.028 + 20);
|
|
} else {
|
|
// 其他国家:amount的值×百分之3加20
|
|
feeValue = Math.round(amount * 0.03 + 20);
|
|
}
|
|
firstdataDTO.setFee((int) feeValue);
|
|
|
|
// net的值为amount减去fee
|
|
firstdataDTO.setNet(amount - (int) feeValue);
|
|
|
|
// 将firstdataDTO存入bankVO
|
|
bankVO.setFirstdataDTO(firstdataDTO);
|
|
|
|
// 创建一个简单的Map来存储提取的数据
|
|
Map<String, Object> extractedData = new HashMap<>();
|
|
extractedData.put("country", country);
|
|
extractedData.put("orderId", orderId);
|
|
extractedData.put("currency", currency);
|
|
extractedData.put("total", total);
|
|
extractedData.put("amount", amount);
|
|
extractedData.put("fee", (int) feeValue);
|
|
extractedData.put("net", amount - (int) feeValue);
|
|
extractedData.put("success", true); // 添加成功标识
|
|
|
|
// 将提取的数据转换为JSON字符串并添加到message列表
|
|
message.add(JSON.toJSONString(extractedData, true));
|
|
|
|
// 将message存入bankVO
|
|
bankVO.setMessage(message);
|
|
|
|
cashCollectionMapper.updateByGoldCoinOrderCodeByFirstdata(bankVO.getFirstdataDTO());
|
|
|
|
return Result.success(bankVO);
|
|
} else {
|
|
// 没有数据时也添加失败标识
|
|
Map<String, Object> emptyData = new HashMap<>();
|
|
emptyData.put("success", false);
|
|
emptyData.put("message", "No data found");
|
|
message.add(JSON.toJSONString(emptyData, true));
|
|
|
|
// 将message存入bankVO
|
|
bankVO.setMessage(message);
|
|
|
|
return Result.error("No data found");
|
|
}
|
|
} catch (Exception e) {
|
|
log.error("Firstdata银行接口调用失败", e);
|
|
|
|
// 在异常情况下也构建包含错误信息的message
|
|
BankVO bankVO = new BankVO();
|
|
List<String> message = new ArrayList<>();
|
|
Map<String, Object> errorData = new HashMap<>();
|
|
errorData.put("success", false);
|
|
errorData.put("error", e.getMessage());
|
|
message.add(JSON.toJSONString(errorData, true));
|
|
bankVO.setMessage(message);
|
|
|
|
return Result.error("Firstdata银行接口调用失败: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
//IPAY银行接口(批量)
|
|
@Override
|
|
public Result ipayAuto(BankDTO bankDTO) {
|
|
// 获取需要处理的订单号列表
|
|
List<CashCollection> cashCollections = cashCollectionMapper.selectIpayList();
|
|
BankVO bankVO = new BankVO();
|
|
List<String> message = new ArrayList<>();
|
|
// 对每个订单执行getFirstdata方法
|
|
for (CashCollection cashCollection : cashCollections) {
|
|
// 创建一个新的BankDTO实例,设置订单号
|
|
Ipay88DTO ipay88DTO = new Ipay88DTO();
|
|
ipay88DTO.setOrderNo(cashCollection.getOrderCode());
|
|
|
|
// 将double先转为int再转为string
|
|
double amountDouble = cashCollection.getPermanentGold() * 3.18;
|
|
int amountInt = (int) amountDouble;
|
|
ipay88DTO.setAmount(String.valueOf(amountInt));
|
|
|
|
double feeDouble = cashCollection.getPermanentGold() * 3.18 * 0.0085;
|
|
int feeInt = (int) feeDouble;
|
|
ipay88DTO.setFee(String.valueOf(feeInt));
|
|
|
|
double netDouble = cashCollection.getPermanentGold() * 3.18 - cashCollection.getPermanentGold() * 3.18 * 0.0085;
|
|
int netInt = (int) netDouble;
|
|
ipay88DTO.setNet(String.valueOf(netInt));
|
|
cashCollectionMapper.updateByGoldCoinOrderCodeByIpay88(ipay88DTO);
|
|
// 构建成功消息
|
|
Map<String, Object> successData = new HashMap<>();
|
|
successData.put("success", true);
|
|
successData.put("orderNo", ipay88DTO.getOrderNo());
|
|
successData.put("amount", ipay88DTO.getAmount());
|
|
successData.put("fee", ipay88DTO.getFee());
|
|
successData.put("net", ipay88DTO.getNet());
|
|
message.add(JSON.toJSONString(successData, true));
|
|
}
|
|
|
|
// 将message存入bankVO
|
|
bankVO.setMessage(message);
|
|
|
|
// 返回处理结果列表
|
|
return Result.success(bankVO);
|
|
}
|
|
|
|
// 银行自动处理接口(每天早上6点执行)
|
|
@Scheduled(cron = "0 53 10 * * ?")
|
|
@Override
|
|
public Result bankAuto() {
|
|
try {
|
|
// 生成昨天的日期,格式为yyyyMMdd
|
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
|
LocalDate sevenDayAgo = LocalDate.now().minusDays(7);
|
|
String sevenDayAgoStr = sevenDayAgo.format(formatter);
|
|
LocalDate sixDayAgo = LocalDate.now().minusDays(6);
|
|
String sixDayAgoStr = sixDayAgo.format(formatter);
|
|
LocalDate fiveDayAgo = LocalDate.now().minusDays(5);
|
|
String fiveDayAgoStr = fiveDayAgo.format(formatter);
|
|
LocalDate fourDayAgo = LocalDate.now().minusDays(4);
|
|
String fourDayAgoStr = fourDayAgo.format(formatter);
|
|
LocalDate threeDayAgo = LocalDate.now().minusDays(3);
|
|
String threeDayAgoStr = threeDayAgo.format(formatter);
|
|
LocalDate twoDayAgo = LocalDate.now().minusDays(2);
|
|
String twoDayAgoStr = twoDayAgo.format(formatter);
|
|
LocalDate oneDayAgo = LocalDate.now().minusDays(1);
|
|
String oneDayAgoStr = oneDayAgo.format(formatter);
|
|
LocalDate today = LocalDate.now();
|
|
String todayStr = today.format(formatter);
|
|
// 创建BankDTO实例并设置时间
|
|
BankDTO dto = new BankDTO();
|
|
|
|
dto.setStartTime(fiveDayAgoStr);
|
|
dto.setEndTime(todayStr);
|
|
dto.setTime(sixDayAgoStr);
|
|
dto.setSum(1000);
|
|
|
|
// 依次调用各个自动处理方法
|
|
Result paymentResult = paymentAuto(dto);
|
|
Result stripeResult = stripeAuto(dto);
|
|
Result firstdataResult = firstdataAuto(dto);
|
|
Result ipayResult = ipayAuto(dto);
|
|
|
|
// 创建响应VO对象并收集处理结果
|
|
BankVO bankVO = new BankVO();
|
|
List<String> messages = new ArrayList<>();
|
|
|
|
// 收集各方法的处理结果信息
|
|
messages.add("Payment Auto Result: " + (paymentResult != null ? paymentResult.toString() : "null"));
|
|
messages.add("Stripe Auto Result: " + (stripeResult != null ? stripeResult.toString() : "null"));
|
|
messages.add("Firstdata Auto Result: " + (firstdataResult != null ? firstdataResult.toString() : "null"));
|
|
messages.add("Ipay Auto Result: " + (ipayResult != null ? ipayResult.toString() : "null"));
|
|
|
|
bankVO.setMessage(messages);
|
|
|
|
return Result.success(bankVO);
|
|
} catch (Exception e) {
|
|
log.error("bankAuto执行失败", e);
|
|
return Result.error("bankAuto执行失败: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* 生成PaymentAsia API所需的签名
|
|
*
|
|
* @return 签名字符串
|
|
*/
|
|
public FirstdataRequestDTO generatePaymentAsiaSignature() {
|
|
try {
|
|
String key = "3E04ZUCKFmQKrW0uoBa89QKIJWYoU9OX";
|
|
String secret = "ZLtBPgfMIT4HXg25SoVuCyUQZ6GtSv9UFmDmYaoVSKS";
|
|
|
|
// 生成ClientRequestId(1-10000000的随机数)
|
|
long clientRequestId = new Random().nextInt(10000000) + 1;
|
|
|
|
// 获取当前时间戳(毫秒)
|
|
long time = System.currentTimeMillis();
|
|
|
|
// 构造原始签名数据
|
|
String rawSignature = key + clientRequestId + time;
|
|
|
|
// 计算HMAC-SHA256
|
|
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
|
|
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
|
|
sha256Hmac.init(secretKey);
|
|
byte[] hmacBytes = sha256Hmac.doFinal(rawSignature.getBytes(StandardCharsets.UTF_8));
|
|
// 转换为Base64编码
|
|
String hmacBase64 = Base64.getEncoder().encodeToString(hmacBytes);
|
|
// 赋值给DTO
|
|
FirstdataRequestDTO firstdataRequestDTO = new FirstdataRequestDTO();
|
|
firstdataRequestDTO.setKey(key);
|
|
firstdataRequestDTO.setSecret(secret);
|
|
firstdataRequestDTO.setClientRequestId(clientRequestId);
|
|
firstdataRequestDTO.setTime(time);
|
|
firstdataRequestDTO.setHmacBase64(hmacBase64);
|
|
|
|
// Base64编码并返回签名
|
|
return firstdataRequestDTO;
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("生成PaymentAsia签名失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* http_build_query的查询字符串(key=value&key=value)
|
|
*/
|
|
private String buildQueryString(Map<String, String> params) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (Map.Entry<String, String> entry : params.entrySet()) {
|
|
if (sb.length() > 0) {
|
|
sb.append("&");
|
|
}
|
|
sb.append(entry.getKey()).append("=").append(entry.getValue());
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
/**
|
|
* SHA512加密
|
|
*/
|
|
private String sha512(String content) throws NoSuchAlgorithmException {
|
|
MessageDigest md = MessageDigest.getInstance("SHA-512");
|
|
byte[] bytes = md.digest(content.getBytes(StandardCharsets.UTF_8));
|
|
// 转换为十六进制字符串
|
|
StringBuilder hexStr = new StringBuilder();
|
|
for (byte b : bytes) {
|
|
String hex = Integer.toHexString(0xff & b);
|
|
if (hex.length() == 1) {
|
|
hexStr.append('0');
|
|
}
|
|
hexStr.append(hex);
|
|
}
|
|
return hexStr.toString();
|
|
}
|
|
|
|
|
|
|
|
}
|