diff --git a/src/main/java/com/example/demo/service/cash/BankService.java b/src/main/java/com/example/demo/service/cash/BankService.java new file mode 100644 index 0000000..ce9c7a2 --- /dev/null +++ b/src/main/java/com/example/demo/service/cash/BankService.java @@ -0,0 +1,40 @@ +package com.example.demo.service.cash; + + +import com.example.demo.domain.DTO.BankDTO; +import com.example.demo.domain.vo.coin.Result; +import com.stripe.exception.StripeException; + +/** + * @program: gold-java + * @ClassName BankService + * @description: + * @author: Double + * @create: 2025−11-21 10:43 + * @Version 1.0 + **/ + +public interface BankService { + //payment银行接口(批量) + Result paymentAuto(BankDTO bankDTO); + + //payment银行接口(单个) + Result getPayment(BankDTO bankDTO); + + //stripe银行接口(批量) + Result stripeAuto(BankDTO bankDTO); + + //stripe银行接口(单个) + Result getStripe(BankDTO bankDTO) throws StripeException; + + //firstdata银行接口(批量) + Result firstdataAuto(BankDTO bankDTO); + + //firstdata银行接口(单个) + Result getFirstdata(BankDTO bankDTO); + + //firstdata银行接口(批量) + Result ipayAuto(BankDTO bankDTO); + //bank银行接口(批量) + Result bankAuto(); +} \ No newline at end of file diff --git a/src/main/java/com/example/demo/serviceImpl/cash/BankServiceImpl.java b/src/main/java/com/example/demo/serviceImpl/cash/BankServiceImpl.java new file mode 100644 index 0000000..c56f282 --- /dev/null +++ b/src/main/java/com/example/demo/serviceImpl/cash/BankServiceImpl.java @@ -0,0 +1,875 @@ +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 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 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> requestEntity = new HttpEntity<>(formData, headers); + + // 调用第三方API(使用配置好SSL协议的RestTemplate) + ResponseEntity 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 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()); + } + } + + public Result paymentAuto(BankDTO bankDTO) { + try { + // 1. 准备参数 + String settlementDate = bankDTO.getTime(); // 从BankDTO对象获取time作为settlement_date + String network = "FPS"; // 固定值 + // 2. 生成签名 + Map 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 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> requestEntity = new HttpEntity<>(formData, headers); + + // 调用第三方API(使用配置好SSL协议的RestTemplate) + ResponseEntity 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"); + + // 添加非空校验 + if (transactions == null || transactions.isEmpty()) { + log.warn("transactions为空或无数据"); + return Result.error("payment当天无数据请切换日期"); + } + + // 创建BankVO并设置paymentDTOList + BankVO bankVO = new BankVO(); + List paymentDTOList; + try { + paymentDTOList = transactions.toJavaList(PaymentDTO.class); + } catch (Exception e) { + log.error("解析JSON响应时发生错误: {}", e.getMessage(), e); + return Result.error("payment当天无数据请切换日期"); + } + bankVO.setPaymentDTOList(paymentDTOList); + // 收集处理信息 + List messages = new ArrayList<>(); + // 对paymentDTOList中的每个元素进行处理 + List 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 orderNoList = cashCollectionMapper.selectPaymentList(); + // 检查当前订单号是否在列表中 + 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.apiKey = "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"; + Stripe.apiKey = "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"; + if (bankDTO.getSum() <= 0) { + return Result.error("最大条数不能小于等于0"); + } + // 收集处理信息 + List messages = new ArrayList<>(); + // 从Stripe获取最近的收费记录(最多200条) + List 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 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 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 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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"; + Stripe.apiKey = "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"; + // 方式一:通过订单号查找最近数据 + String orderNo = bankDTO.getOrderNo(); + + if (bankDTO.getSum() <= 0) { + return Result.error("最大条数不能小于等于0"); + } + + // 从Stripe获取最近的收费记录(最多200条) + List 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 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 orderNoList = cashCollectionMapper.selectFirstdataList(); + + // 存储处理结果的列表 + List 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 entity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + entity, + String.class + ); + + // 解析响应数据 + // API返回的是对象格式,需要先解析为JSONObject + JSONObject jsonObject = JSON.parseObject(response.getBody()); + + // 创建BankVO对象并设置数据 + BankVO bankVO = new BankVO(); + List 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 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 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 message = new ArrayList<>(); + Map 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 cashCollections = cashCollectionMapper.selectIpayList(); + BankVO bankVO = new BankVO(); + List 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) Math.round(amountDouble); + ipay88DTO.setAmount(String.valueOf(amountInt)); + + double feeDouble = cashCollection.getPermanentGold() * 3.18 * 0.0085; + int feeInt = (int) Math.round(feeDouble); + ipay88DTO.setFee(String.valueOf(feeInt)); + + int netInt = amountInt-feeInt; + ipay88DTO.setNet(String.valueOf(netInt)); + cashCollectionMapper.updateByGoldCoinOrderCodeByIpay88(ipay88DTO); + // 构建成功消息 + Map 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 54 15 * * ?") + @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.setSum(1000); + + // 依次调用各个自动处理方法 + dto.setTime(oneDayAgoStr); + Result paymentOneDayResult = paymentAuto(dto); + dto.setTime(twoDayAgoStr); + Result paymentTwoDayResult = paymentAuto(dto); + dto.setTime(threeDayAgoStr); + Result paymentThreeDayResult = paymentAuto(dto); + dto.setTime(fourDayAgoStr); + Result paymentFourDayResult = paymentAuto(dto); + dto.setTime(fiveDayAgoStr); + Result paymentFiveDayResult = paymentAuto(dto); + dto.setTime(sixDayAgoStr); + Result paymentSixDayResult = paymentAuto(dto); + dto.setTime(sevenDayAgoStr); + Result paymentSevenDayResult = paymentAuto(dto); + Result stripeResult = stripeAuto(dto); + Result firstdataResult = firstdataAuto(dto); + Result ipayResult = ipayAuto(dto); + + // 创建响应VO对象并收集处理结果 + BankVO bankVO = new BankVO(); + List messages = new ArrayList<>(); + + // 收集各方法的处理结果信息 + messages.add("Payment One Day Auto Result: " + (paymentOneDayResult != null ? paymentOneDayResult.toString() : "null")); + messages.add("Payment Two Day Auto Result: " + (paymentTwoDayResult != null ? paymentTwoDayResult.toString() : "null")); + messages.add("Payment Three Day Auto Result: " + (paymentThreeDayResult != null ? paymentThreeDayResult.toString() : "null")); + messages.add("Payment Four Day Auto Result: " + (paymentFourDayResult != null ? paymentFourDayResult.toString() : "null")); + messages.add("Payment Five Day Auto Result: " + (paymentFiveDayResult != null ? paymentFiveDayResult.toString() : "null")); + messages.add("Payment Six Day Auto Result: " + (paymentSixDayResult != null ? paymentSixDayResult.toString() : "null")); + messages.add("Payment Seven Day Auto Result: " + (paymentSevenDayResult != null ? paymentSevenDayResult.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 params) { + StringBuilder sb = new StringBuilder(); + for (Map.Entry 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(); + } + + + +} \ No newline at end of file