支付系统就一定需要对账,需要下载支付平台侧的账单与自己业务系统的交易数据时行对账。
支付宝账单:为方便商户快速查账,支持商户通过本接口获取商户离线账单下载地址。
交易账单
支付宝的对账API 是在 财务 API 分类里面。
下载支付宝交易对单账分两步:
向查询对账单下载地址的接口发请求,返回数据中包含下载账单的 URL 地址。
这个 URL 是一个账单压缩文件的下载地址,可直接在浏览器访问下载。
下载下来的是一个以 账号_交易日期 为名的压缩包。
压缩包中有 2 个以 csv 后缀的文件,实际就是文本表格文件,可用文本编辑器打开。一个是 业务明细,另一个是 业务明细(汇总)。
文本表格内容包含了
\r\n换行符,明细数据行首字母不是#号。见下方的账单数据示例
账单数据
账单业务明细示例:
#支付宝业务明细查询
#账号:[208xxxxxxxxxxxx156]
#起始日期:[2020年07月22日 00:00:00] 终止日期:[2020年07月23日 00:00:00]
#-----------------------------------------业务明细列表----------------------------------------
支付宝交易号,商户订单号,业务类型,商品名称,创建时间,完成时间,门店编号,门店名称,操作员,终端号,对方账户,订单金额(元),商家实收(元),支付宝红包(元),集分宝(元),支付宝优惠(元),商家优惠(元),券核销金额(元),券名称,商家红包消费金额(元),卡消费金额(元),退款批次号/请求号,服务费(元),分润(元),备注
20200xxxxxxxxxxxxxx44491 ,411735xxxxxxxxxxxxxx720 ,交易 ,院内充值,2020-07-22 16:13:11,2020-07-22 16:14:24, , , , ,**佳(yan***@163.com) ,1.00,1.00,0.96,0.00,0.00,0.00,0.00,花呗mau生活费实体店红包,0.00 ,0.00, ,-0.01,0.00,
#-----------------------------------------业务明细列表结束------------------------------------
#交易合计:1笔,商家实收共1.00元,商家优惠共0.00元
#退款合计:0笔,商家实收退款共0.00元,商家优惠退款共0.00元
#导出时间:[2020年07月23日 04:40:08]
账单解析
实体类
/**
* @desc: 支付宝账单明细
*/
@Data
@SuperBuilder
@Accessors(chain = true)
public class AliPayBillDetail implements Serializable {
private static final long serialVersionUID = 9122205774630735303L;
/**
* 支付宝交易号
*/
private String tradeNo;
/**
* 商户订单号
*/
private String outTradeNo;
/**
* 业务类型
*/
private String businessType;
/**
* 商品名称
*/
private String tradeName;
/**
* 创建时间
*/
private String createTime;
/**
* 完成时间
*/
private String finishTime;
/**
* 门店编号
*/
private String storeNumber;
/**
* 门店名称
*/
private String storeName;
/**
* 操作员
*/
private String operator;
/**
* 终端号
*/
private String terminalNumber;
/**
* 对方账户
*/
private String clientAccount;
/**
* 订单金额(元)
*/
private String orderAmount;
/**
* 商家实收(元)
*/
private String realAmount;
/**
* 支付宝红包(元)
*/
private String redPaperAmount;
/**
* 集分宝(元)
*/
private String jifenbaoAmount;
/**
* 支付宝优惠(元)
*/
private String zfbDiscountAmount;
/**
* 商家优惠(元)
*/
private String merchantOffersAmount;
/**
* 券核销金额(元)
*/
private String CouponWriteOffAmount;
/**
* 券名称
*/
private String couponName;
/**
* 商家红包消费金额(元)
*/
private String merchantRedAmount;
/**
* 卡消费金额(元)
*/
private String cardAmount;
/**
* 退款批次号/请求号
*/
private String refundNo;
/**
* 服务费(元)
*/
private String serviceFee;
/**
* 分润(元)
*/
private String fenrun;
/**
* 备注
*/
private String remark;
}
数据解析
/**
* @desc: 下载支付宝账单业务
*/
@Service
public class AliPayBillServiceImpl implements AliPayBillService {
private static final Logger logger = LogManager.getLogger(AliPayBillServiceImpl.class);
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
@Autowired
private PayAccountService payAccountService;
@Autowired
private PayProperties payProperties;
private static final String aliPayDataFormat = "JSON";
private static final String defaultCharset = "utf-8";
private static final String defaultSignType = "RSA2";
/**
* 下载支付宝账单
*
* @param downloadVO
*/
@Override
public HashMap<String, List<AliPayBillDetail>> downloadAliPayBill(DownloadVO downloadVO) {
List<PayAccountBO> payAccountList = getOrgPayAccount(downloadVO);
HashMap<String, List<AliPayBillDetail>> hashMap = new HashMap<>();
payAccountList.parallelStream().forEach(payAccount -> {
String downloadBillUrl = this.getBillFileUrl(downloadVO, payAccount);
logger.info("获取 {} 支付宝账单下载的URL:{}", downloadVO.getBillDate(), downloadBillUrl);
if (StringUtils.isNotBlank(downloadBillUrl)) {
List<AliPayBillDetail> aliPayBillDetails = downloadBillFile(downloadBillUrl);
hashMap.put(payAccount.getAppId(), aliPayBillDetails);
}
});
return hashMap;
}
/**
* @desc: 获取下载账单的URL
* @param: [downloadVO, payAccount]
*/
private String getBillFileUrl(DownloadVO downloadVO, PayAccountBO payAccount) {
AlipayClient alipayClient = getByAccount(payAccount);
AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest();
HashMap<String, String> paramsMap = new HashMap<>();
paramsMap.put("bill_type", "trade");
paramsMap.put("bill_date", downloadVO.getBillDate());
request.setBizContent(JSON.toJSONString(paramsMap));
AlipayDataDataserviceBillDownloadurlQueryResponse response = null;
try {
response = alipayClient.execute(request);
if (response.isSuccess()) {
return response.getBillDownloadUrl();
}
} catch (AlipayApiException e) {
throw new BusinessException(e.getMessage());
}
return StringUtils.EMPTY;
}
/**
* AlipayClient
*
* @param payAccount
* @return
*/
public AlipayClient getByAccount(PayAccount payAccount) {
return new DefaultAlipayClient(payProperties.getAliPayGateway(), payAccount.getAppId(),
payAccount.getAliPrivateKey(), aliPayDataFormat, defaultCharset, payAccount.getAliPublicKey(), defaultSignType);
}
/**
* 下载账单文件
*
* @param downloadUrl
* @return
*/
public List<AliPayBillDetail> downloadBillFile(String downloadUrl) {
List<AliPayBillDetail> aliPayBillDetails = new ArrayList<>();
HttpURLConnection conn = null;
ZipInputStream in = null;
BufferedReader br = null;
try {
URL url = new URL(downloadUrl);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5 * 1000);
conn.setRequestMethod("GET");
conn.connect();
// 不解压直接读取,加上GBK解决乱码问题
in = new ZipInputStream(conn.getInputStream(), Charset.forName("GBK"));
br = new BufferedReader(new InputStreamReader(in, "GBK"));
ZipEntry zipFile;
// 循环读取zip中的cvs文件,无法使用jdk自带,因为文件名中有中文
while ((zipFile = in.getNextEntry()) != null) {
if (zipFile.isDirectory()) {
// 目录不处理
}
// 获得cvs名字,检测文件是否存在
String fileName = zipFile.getName();
logger.info("对账单解析,输出文件名称:{}", fileName);
// 压缩包有业务明细和业务明细汇总2个文件,对账用到明细数据,所以排除汇总文件
if (!Objects.isNull(fileName) && fileName.contains(".") && !fileName.contains("汇总")) {
String line;
int i = 0;
// 按行读取数据
while ((line = br.readLine()) != null) {
// 数据行不以 # 为开头
if (!line.startsWith("#")) {
logger.info("解析数据行:{}", line);
// 数据从第2行开始读,第1行是表头
if (i > 0) {
String[] lines = line.split(",", -1);
AliPayBillDetail aliPayBillDetail = AliPayBillDetail.builder()
.tradeNo(lines[0].trim())
.outTradeNo(lines[1].trim())
.businessType(lines[2].trim())
.tradeName(lines[3].trim())
.createTime(lines[4].trim())
.finishTime(lines[5].trim())
.storeNumber(lines[6].trim())
.storeName(lines[7].trim())
.operator(lines[8].trim())
.terminalNumber(lines[9].trim())
.clientAccount(lines[10].trim())
.orderAmount(lines[11].trim())
.realAmount(lines[12].trim())
.redPaperAmount(lines[13].trim())
.jifenbaoAmount(lines[14].trim())
.zfbDiscountAmount(lines[15].trim())
.merchantOffersAmount(lines[16].trim())
.CouponWriteOffAmount(lines[17].trim())
.couponName(lines[18].trim())
.merchantRedAmount(lines[19].trim())
.cardAmount(lines[20].trim())
.refundNo(lines[21].trim())
.serviceFee(lines[22].trim())
.fenrun(lines[23].trim())
.remark(lines[24].trim())
.build();
aliPayBillDetails.add(aliPayBillDetail);
}
i++;
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) br.close();
if (in != null) in.close();
if (conn != null) conn.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
return aliPayBillDetails;
}
}
更多内容请访问:IT源点
注意:本文归作者所有,未经作者允许,不得转载