企业数字化转型中,支付系统与 ERP 的对接核心是围绕支付全链路数据流 的打通。常见的支付类接口系统通常以支付为核心,通过下单接口 发起交易、查询接口 获取状态、通知接口推送结果,形成完整的支付数据流闭环。本文基于这些核心接口,详解如何实现从订单创建到财务对账的全流程自动化,确保业务与财务数据的一致性。
一、整体对接架构:以支付接口为核心的数据流闭环
- 下单接口 (
[api/v3/ccss/counter/order/special_create]
):创建支付订单,生成支付凭证 - 查询接口 (
[api/v3/ccss/counter/order/query]
):主动查询订单支付状态 - 通知接口(回调地址):异步推送支付结果(支付成功 / 失败)
这些接口构成了支付系统与 ERP 对接的核心数据通道,整体架构需实现三大目标:
- 订单信息通过下单接口同步至支付系统
- 支付结果通过通知 / 查询接口反馈至 ERP
- 交易数据通过对账接口实现财务核对
架构分层与接口映射
对接环节 | 核心功能 | 开放平台核心接口(*聚合收银台为例) | 数据流向 |
---|---|---|---|
订单与支付发起 | ERP 订单同步至支付系统,发起支付 | 下单接口[api/v3/ccss/counter/order/special_create] |
ERP → 支付系统(拉卡拉) |
支付状态同步 | 支付结果反馈至 ERP 系统 | 通知接口 + 查询接口 | 支付系统 → ERP |
财务对账 | 交易数据与 ERP 财务数据比对 | 对账接口 [api/v3/ccss/counter/order/query] |
双向比对 |
二、订单同步与支付发起:基于下单接口的流程设计
订单同步的核心是将 ERP 系统的订单信息通过拉卡拉下单接口传递至支付系统,生成可支付的订单凭证(如二维码、支付链接),为用户支付环节提供基础。
(一)核心流程与接口参数
-
订单数据准备
ERP 系统需整理订单核心信息,包括:
merchant_no
(商户编号):拉卡拉分配的唯一标识out_order_no
(商户订单号):ERP 系统生成的唯一订单号total_amount
(订单金额):单位为元,精确到分order_efficient_time
(订单有效期):格式yyyyMMddHHmmssorder_info
(商品名称):订单标题,不超过 64 字符notifyUrl
(回调地址):支付结果通知接收地址(必填)
-
接口调用逻辑
ERP 系统通过 HTTP POST 请求调用拉卡拉下单接口,参数以 JSON 格式传递,并通过签名机制确保数据安全。接口返回结果包含
qrCode
(支付二维码)或payUrl
(支付链接),ERP 系统将其下发至用户端完成支付。
(二)Java 下单接口调用示例
java
public class PaymentInitiator {
// 拉卡拉下单接口地址
private static final String CREATE_ORDER_URL = "https://open.lakala.com/api/pay/create";
private static final String API_KEY = "your_api_key"; // 商户密钥
private static final String MERCHANT_NO = "M20230700001";
/**
* 调用拉卡拉下单接口,创建支付订单
*/
public PayResult createPaymentOrder(ERPOrder erpOrder) throws Exception {
// 1. 构建请求参数
JSONObject requestParams = new JSONObject();
requestParams.put("merchantNo", MERCHANT_NO);
requestParams.put("outTradeNo", erpOrder.getOrderNo()); // ERP订单号
requestParams.put("totalAmount", erpOrder.getAmount()); // 订单金额(元)
requestParams.put("subject", erpOrder.getGoodsName());
requestParams.put("notifyUrl", "https://your-domain.com/payment/notify"); // 回调地址
requestParams.put("timestamp", System.currentTimeMillis() / 1000); // 秒级时间戳
// 2. 生成签名(拉卡拉标准签名算法)
String sign = generateSign(requestParams);
requestParams.put("sign", sign);
// 3. 发送HTTPS请求
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(CREATE_ORDER_URL);
httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
httpPost.setEntity(new StringEntity(requestParams.toString(), "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
String result = EntityUtils.toString(response.getEntity(), "UTF-8");
response.close();
httpClient.close();
// 4. 解析响应结果
JSONObject resultJson = JSONObject.parseObject(result);
if ("0000".equals(resultJson.getString("code"))) {
// 下单成功,返回支付凭证
JSONObject data = resultJson.getJSONObject("data");
return new PayResult(
erpOrder.getOrderNo(),
data.getString("qrCode"), // 支付二维码
data.getString("tradeNo") // 拉卡拉交易号
);
} else {
// 下单失败,抛出异常
throw new RuntimeException("下单失败:" + resultJson.getString("msg"));
}
}
// 签名生成(按拉卡拉要求:参数ASCII排序+MD5加密)
private String generateSign(JSONObject params) throws Exception {
Set<String> keySet = params.keySet();
List<String> keyList = new ArrayList<>(keySet);
Collections.sort(keyList); // ASCII升序排序
StringBuilder sb = new StringBuilder();
for (String key : keyList) {
if (!"sign".equals(key) && params.get(key) != null) {
sb.append(key).append("=").append(params.getString(key)).append("&");
}
}
sb.append("key=").append(API_KEY);
// MD5加密并转为大写
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(sb.toString().getBytes("UTF-8"));
StringBuilder signBuilder = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) signBuilder.append("0");
signBuilder.append(hex);
}
return signBuilder.toString().toUpperCase();
}
}
(三)异常处理机制
- 接口调用超时:设置 30 秒超时时间,超时后自动重试(最多 3 次),重试间隔依次为 1s、3s、5s
- 下单失败处理:记录失败日志,标记订单状态为 "支付发起失败",支持手动触发重新下单
- 幂等性保障 :通过
outTradeNo
(商户订单号)确保唯一,避免重复下单
三、支付状态同步:查询接口与通知接口的协同
支付状态同步是确保 ERP 系统及时获取交易结果的关键,通过通知接口 (异步推送)和查询接口(主动查询)双重机制,保障状态更新的及时性与准确性。
(一)通知接口:异步接收支付结果
拉卡拉支付系统在用户完成支付后,会主动向notifyUrl
推送支付结果,这是状态同步的主要方式。
-
通知数据格式
回调数据包含以下核心字段
:
outTradeNo
:商户订单号(ERP 订单号)tradeNo
:拉卡拉交易号tradeStatus
:交易状态(SUCCESS
表示成功)totalAmount
:支付金额trade_time
:交易完时间
-
回调处理实现(Spring Boot)
typescript
@RestController
public class PaymentNotifyController {
private static final String API_KEY = "your_api_key";
@Autowired
private OrderService orderService;
@PostMapping("/payment/notify")
public String handlePaymentNotify(@RequestBody JSONObject notifyData) {
// 1. 记录原始通知数据(用于问题排查)
log.info("支付结果通知:" + notifyData.toString());
// 2. 验证签名(防止伪造请求)
if (!verifySign(notifyData)) {
log.error("签名验证失败:" + notifyData.getString("outTradeNo"));
return "fail"; // 签名失败,拉卡拉会重试通知
}
// 3. 解析通知数据
String orderNo = notifyData.getString("outTradeNo");
String tradeStatus = notifyData.getString("tradeStatus");
String tradeNo = notifyData.getString("tradeNo");
String payTime = notifyData.getString("gmtPayment");
// 4. 更新订单状态(核心业务逻辑)
if ("SUCCESS".equals(tradeStatus)) {
boolean updateResult = orderService.updateOrderToPaid(
orderNo, tradeNo, payTime, notifyData.getString("totalAmount")
);
if (updateResult) {
return "success"; // 处理成功,拉卡拉停止重试
}
}
// 处理失败,拉卡拉会在24小时内重试(最多8次)
return "fail";
}
// 签名验证(与下单接口签名算法一致)
private boolean verifySign(JSONObject data) {
String receivedSign = data.getString("sign");
if (receivedSign == null) return false;
// 移除sign字段后重新计算签名
JSONObject tempData = (JSONObject) data.clone();
tempData.remove("sign");
String calculatedSign = generateSign(tempData); // 复用下单接口的签名方法
return receivedSign.equals(calculatedSign);
}
}
二)查询接口:主动获取支付状态
为应对通知接口可能的延迟或丢失,需通过查询接口主动校验订单状态,作为补充机制。
- 查询时机
- 下单后 30 分钟未收到回调通知
- 用户反馈支付成功但系统未更新状态
- 每日定时对 "待支付" 订单进行批量查询
- 查询接口调用实现
typescript
public class PaymentQueryClient {
private static final String QUERY_URL = "https://open.lakala.com/api/pay/query";
private static final String API_KEY = "your_api_key";
/**
* 查询订单支付状态
*/
public QueryResult queryPaymentStatus(String orderNo) throws Exception {
// 1. 构建查询参数
JSONObject queryParams = new JSONObject();
queryParams.put("merchantNo", "M20230700001");
queryParams.put("outTradeNo", orderNo); // 商户订单号
queryParams.put("sign", generateSign(queryParams)); // 生成签名
// 2. 调用查询接口
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(QUERY_URL);
httpPost.setHeader("Content-Type", "application/json");
httpPost.setEntity(new StringEntity(queryParams.toString(), "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
String result = EntityUtils.toString(response.getEntity(), "UTF-8");
response.close();
httpClient.close();
// 3. 解析查询结果
JSONObject resultJson = JSONObject.parseObject(result);
if ("0000".equals(resultJson.getString("code"))) {
JSONObject data = resultJson.getJSONObject("data");
return new QueryResult(
data.getString("outTradeNo"),
data.getString("tradeStatus"), // SUCCESS/REFUND/CLOSED等
data.getString("gmtPayment")
);
} else {
throw new RuntimeException("查询失败:" + resultJson.getString("msg"));
}
}
}
(三)双重机制保障
- 优先依赖通知接口:实时性高,减少主动查询的资源消耗
- 定时查询补充:对超过 30 分钟未收到通知的订单,调用查询接口主动获取状态,避免漏单
四、财务对账:基于对账接口的自动化核对
财务对账通过拉卡拉对账接口获取官方交易记录,与 ERP 系统的收款记录比对,确保资金数据一致
(一)对账流程设计
- 数据获取
每日凌晨调用拉卡拉对账接口(/api/v3/bmmp4/checkFile/apply
),获取前一天的全量交易记录,包含:
- 正常支付订单:订单号、金额、手续费、支付时间
- 退款订单:退款金额、退款时间、原订单号
- 结算信息:实际到账金额、结算日期
- 比对规则
比对维度 | 规则说明 | 差异处理方式 |
---|---|---|
订单存在性 | 双方订单号必须匹配 | 标记 "漏单" 或 "虚增订单" |
金额一致性 | 支付金额、退款金额需完全一致 | 标记 "金额不符",触发财务核查 |
状态一致性 | 交易状态(成功 / 失败 / 退款)需一致 | 标记 "状态不符",人工核实 |
(二)对账任务实现(Quartz)
typescript
public class ReconciliationJob implements Job {
private static final String RECONCILIATION_URL = "https://open.lakala.com/api/pay/reconciliation";
private static final String API_KEY = "your_api_key";
@Autowired
private ReconciliationService reconciliationService;
@Override
public void execute(JobExecutionContext context) {
try {
// 1. 计算对账日期(前一天)
LocalDate checkDate = LocalDate.now().minusDays(1);
String dateStr = checkDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
// 2. 调用拉卡拉对账接口获取数据
List<JSONObject> lakalaRecords = getLakalaReconciliationData(dateStr);
// 3. 获取ERP系统收款记录
List<JSONObject> erpRecords = reconciliationService.getErpPaymentRecords(checkDate);
// 4. 执行比对逻辑
ReconciliationResult result = reconciliationService.compareRecords(
lakalaRecords, erpRecords, checkDate
);
// 5. 处理比对结果
if (result.hasDiscrepancies()) {
// 保存差异记录
reconciliationService.saveDiscrepancies(result.getDiscrepancies());
// 发送差异告警
notificationService.sendReconciliationAlert(result);
} else {
log.info("对账完成,日期:" + dateStr + ",无差异");
}
} catch (Exception e) {
log.error("对账任务执行失败", e);
notificationService.sendSystemAlert("对账任务异常:" + e.getMessage());
}
}
// 调用拉卡拉对账接口
private List<JSONObject> getLakalaReconciliationData(String dateStr) throws Exception {
JSONObject params = new JSONObject();
params.put("merchantNo", "M20230700001");
params.put("billDate", dateStr); // 对账日期(yyyyMMdd)
params.put("sign", generateSign(params)); // 签名
// 发送请求(HTTP实现略,参考下单接口)
String response = httpClient.post(RECONCILIATION_URL, params.toString());
JSONObject resultJson = JSONObject.parseObject(response);
if ("0000".equals(resultJson.getString("code"))) {
return resultJson.getJSONArray("data").toJavaList(JSONObject.class);
} else {
throw new RuntimeException("获取对账数据失败:" + resultJson.getString("msg"));
}
}
}
五、关键保障措施
(一)接口调用的稳定性保障
-
超时与重试机制
- 所有接口调用设置 30 秒超时
- 非幂等接口(如退款)不重试,幂等接口(如下单、查询)最多重试 3 次
-
限流保护
- 控制接口调用频率(如每秒不超过 100 次),避免触发拉卡拉平台限流
- 大促期间提前申请接口调用额度提升
(二)数据安全保障
-
签名与加密
- 所有接口参数必须通过签名验证,防止数据篡改
- 敏感信息(如用户手机号)传输前加密,落地存储时脱敏
-
日志与审计
- 记录所有接口调用日志(请求参数、响应数据、处理结果),保留至少 6 个月
- 定期审计接口调用记录,排查异常请求
(三)监控与告警
- 接口健康度监控:调用成功率、响应时间(阈值:成功率≥99.9%,响应时间 < 500ms)
- 业务指标监控:订单支付成功率、对账差异率
- 异常告警:接口连续失败、对账差异金额超阈值时,通过短信 / 钉钉实时通知
总结
拉卡拉开放平台的支付接口(下单、查询、通知、对账)构成了支付系统与 ERP 对接的核心骨架。通过订单同步触发下单接口、支付结果依赖通知与查询接口、财务对账基于对账接口的流程设计,可实现全链路自动化。
企业在落地时需注意:
- 优先使用通知接口同步状态,辅以查询接口确保数据完整
- 严格遵守签名机制,保障接口调用安全
- 对账环节需覆盖全量交易(包括退款、冲正),确保资金数据准确
通过这套方案,企业可将订单处理时效提升至秒级,对账效率提升 90% 以上,显著降低人工成本与财务风险,为数字化转型提供坚实的支付数据支撑。
大家在对接支付接口时都遇到过哪些问题?评论区留言,下一篇将会针对性讲解~