Browse Source

12月2日银行接口同步service

sunjiabei/feature-20251202094523-银行接口同步
sunjiabei 1 month ago
parent
commit
32175e6deb
  1. 30
      src/main/java/com/example/demo/service/cash/BankService.java
  2. 520
      src/main/java/com/example/demo/serviceImpl/cash/BankServiceImpl.java

30
src/main/java/com/example/demo/service/cash/BankService.java

@ -0,0 +1,30 @@
package com.example.demo.service.cash;
import com.example.demo.domain.DTO.BankDTO;
import com.example.demo.domain.vo.cash.BankVO;
import com.example.demo.domain.vo.coin.Result;
import com.stripe.exception.StripeException;
/**
* @program: gold-java
* @ClassName BankService
* @description:
* @author: Double
* @create: 202511-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;
}

520
src/main/java/com/example/demo/serviceImpl/cash/BankServiceImpl.java

@ -0,0 +1,520 @@
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.Util.DateConvertUtil;
import com.example.demo.domain.DTO.BankDTO;
import com.example.demo.domain.DTO.PaymentDTO;
import com.example.demo.domain.DTO.StripeDTO;
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.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @program: gold-java
* @ClassName BankServiceImpl
* @description:
* @author: Double
* @create: 202511-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 = transactions.toJavaList(PaymentDTO.class);
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("2");
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 = transactions.toJavaList(PaymentDTO.class);
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("2");
// 获取订单号
String orderNo = paymentDTO.getMerchant_reference();
// 查询金币系统中的订单
CashCollection cashCollection = cashCollectionMapper.selectByGoldCoinOrderCode(orderNo);
if (cashCollection != null) {
// 更新金币系统中的订单信息
cashCollectionMapper.updateByGoldCoinOrderCodeByPayment(paymentDTO);
processedPayments.add(paymentDTO);
log.info("成功处理订单: {}", orderNo);
messages.add("成功处理订单: " + orderNo);
} else {
log.warn("金币系统中未找到订单: {}", orderNo);
messages.add("金币系统中未找到订单: " + orderNo);
}
} catch (Exception e) {
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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";
// 从Stripe获取最近的收费记录最多200条
List<Charge> allCharges = new ArrayList<>();
String startingAfter = null;
int totalLimit = 200; // 目标获取条数
int pageSize = 100; // 单次最大获取条数
do {
// 计算当前页需查询的条数最后一页可能不足100条
int currentPageSize = Math.min(pageSize, totalLimit - allCharges.size());
if (currentPageSize <= 0) {
break; // 已获取足够条数停止
}
// 构建分页参数
ChargeListParams params = ChargeListParams.builder()
.setLimit((long) currentPageSize)
.setStartingAfter(startingAfter)
.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(charge.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);
// 如果订单号存在则更新数据库中的记录
if (stripeDTO.getOrderNo() != null && !stripeDTO.getOrderNo().isEmpty()) {
CashCollection cashCollection = cashCollectionMapper.selectByGoldCoinOrderCode(stripeDTO.getOrderNo());
if (cashCollection != null) {
cashCollectionMapper.updateByGoldCoinOrderCodeByStripe(stripeDTO);
}
}
} catch (Exception e) {
log.error("处理Stripe数据失败,chargeId: " + charge.getId(), e);
// 继续处理其他数据不中断整个流程
}
}
// 创建响应VO对象
BankVO bankVO = new BankVO();
bankVO.setStripeDTOList(stripeDTOList);
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 (orderNo == null || orderNo.isEmpty()) {
return Result.error("订单号为空");
}
// 从Stripe获取最近的收费记录最多200条
List<Charge> allCharges = new ArrayList<>();
String startingAfter = null;
int totalLimit = 200; // 目标获取条数
int pageSize = 100; // 单次最大获取条数
do {
// 计算当前页需查询的条数最后一页可能不足100条
int currentPageSize = Math.min(pageSize, totalLimit - allCharges.size());
if (currentPageSize <= 0) {
break; // 已获取足够条数停止
}
// 构建分页参数
ChargeListParams params = ChargeListParams.builder()
.setLimit((long) currentPageSize)
.setStartingAfter(startingAfter)
.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;
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
stripeDTO.setCurrency(matchedCharge.getCurrency().toUpperCase());
stripeDTO.setAmount(String.valueOf(matchedCharge.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());
}
}
/**
* 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();
}
}
Loading…
Cancel
Save