Java对接拉卡拉支付完整指南

Java对接拉卡拉支付完整指南

目录

  1. 概述
  2. 环境准备
  3. SDK初始化配置
  4. 支付接口实现
  5. 回调处理
  6. 完整代码示例
  7. 注意事项
  8. 常见问题

概述

拉卡拉支付是中国领先的第三方支付解决方案提供商。本文档详细介绍如何在Java Spring Boot项目中集成拉卡拉支付SDK,实现收银台订单创建和支付回调处理功能。

功能特性

  • ✅ 收银台订单创建(全报文加密)
  • ✅ 支付回调通知处理
  • ✅ 签名验证
  • ✅ 订单状态管理

1.环境准备

接入官方sdk

在这里插入图片描述
 <!-- 拉卡拉支付依赖--> <dependency> <groupId>com.lkl.laop.sdk</groupId> <artifactId>lkl-laop-java-sdk</artifactId> <version>1.0.8</version> <systemPath>${project.basedir}/src/main/resources/lib/lkl-java-sdk-1.0.8.jar</systemPath> <scope>system</scope> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.3</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.68</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk15on</artifactId> <version>1.68</version> </dependency> 

导入依赖 然后根据sdk写文档拉起支付

2. 证书准备

需要从拉卡拉官方获取以下证书文件:

  • 商户私钥证书(PEM格式)
  • 拉卡拉平台证书(PEM格式)
  • 拉卡拉平台通知证书(PEM格式)

SDK初始化配置

1. 配置文件 (application.yml)

application.yml 中添加拉卡拉支付配置:

# 拉卡拉支付SDK配置lakala:sdk:# 拉卡拉应用IDapp-id:"OP00000003"# 商户证书序列号serial-no:"dfba8194c41b84cf"# 商户私钥(PEM格式字符串)private-key:| -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDvDBZyHUDndAGx rIcsCV2njhNO3vCEZotTaWYSYwtDvkcAb1EjsBFabXZaKigpqFXk5XXNI3NIHP9M ...(私钥内容) -----END PRIVATE KEY-----# 拉卡拉平台证书(用于验证API响应签名)lkl-certificate:| -----BEGIN CERTIFICATE----- MIIEMTCCAxmgAwIBAgIGAXRTgcMnMA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYT ...(平台证书内容) -----END CERTIFICATE-----# 拉卡拉平台通知证书(用于验证异步通知签名)lkl-notify-certificate:| -----BEGIN CERTIFICATE----- MIIEMTCCAxmgAwIBAgIGAXRTgcMnMA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYT ...(通知证书内容) -----END CERTIFICATE-----# SM4加密密钥(Base64编码)sm4-key:"LHo55AjrT4aDhAIBZhb5KQ=="# API服务器地址server-url:"https://test.wsmsd.cn/sit"# 连接超时时间(毫秒)connect-timeout:50000# 读取超时时间(毫秒)read-timeout:100000# Socket超时时间(毫秒)socket-timeout:50000

2. SDK初始化类 (LakalaSdkConfig.java)

创建SDK初始化配置类:

packagecom.shunp.framework.config;importcom.laop.sdk.Config2;importcom.laop.sdk.LKLSDK;importjakarta.annotation.PostConstruct;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Configuration;/** * 拉卡拉SDK配置类 */@Configuration@Slf4jpublicclassLakalaSdkConfig{@Value("${lakala.sdk.app-id:}")privateString appId;@Value("${lakala.sdk.serial-no:}")privateString serialNo;@Value("${lakala.sdk.private-key:}")privateString privateKey;@Value("${lakala.sdk.lkl-certificate:}")privateString lklCertificate;@Value("${lakala.sdk.lkl-notify-certificate:}")privateString lklNotifyCertificate;@Value("${lakala.sdk.sm4-key:}")privateString sm4Key;@Value("${lakala.sdk.server-url:}")privateString serverUrl;@Value("${lakala.sdk.connect-timeout:50000}")privateInteger connectTimeout;@Value("${lakala.sdk.read-timeout:100000}")privateInteger readTimeout;@Value("${lakala.sdk.socket-timeout:50000}")privateInteger socketTimeout;/** * 初始化LKLSDK */@PostConstructpublicvoidinitLklSdk(){try{ log.info("开始初始化拉卡拉SDK...");// 校验必要参数if(appId ==null|| appId.trim().isEmpty()){ log.warn("拉卡拉SDK appId未配置,跳过初始化");return;}// ... 其他参数校验// 创建Config2对象Config2 config =newConfig2(); config.setAppId(appId.trim()); config.setSerialNo(serialNo.trim()); config.setPriKey(privateKey.trim()); config.setLklCer(lklCertificate.trim()); config.setLklNotifyCer(lklNotifyCertificate.trim()); config.setServerUrl(serverUrl.trim());// 可选参数if(sm4Key !=null&&!sm4Key.trim().isEmpty()){ config.setSm4Key(sm4Key.trim());}if(connectTimeout !=null){ config.setConnectTimeout(connectTimeout);}if(readTimeout !=null){ config.setReadTimeout(readTimeout);}if(socketTimeout !=null){ config.setSocketTimeout(socketTimeout);}// 初始化SDKboolean initResult = LKLSDK.init(config);if(initResult){ log.info("拉卡拉SDK初始化成功,appId: {}", appId);}else{ log.error("拉卡拉SDK初始化失败,appId: {}", appId);}}catch(Exception e){ log.error("拉卡拉SDK初始化异常", e);}}}

支付接口实现

1. 支付Controller (LakalaPayController.java)

packagecom.shunp.web.controller.pay;importcom.alibaba.fastjson2.JSONObject;importcom.laop.sdk.LKLSDK;importcom.laop.sdk.exception.SDKException;importcom.laop.sdk.request.V3CcssCounterOrderSpecialCreateEncryRequest;importcom.shunp.common.core.controller.BaseController;importcom.shunp.common.core.domain.AjaxResult;importio.swagger.v3.oas.annotations.Operation;importio.swagger.v3.oas.annotations.tags.Tag;importlombok.Data;importlombok.extern.slf4j.Slf4j;importorg.springframework.web.bind.annotation.*;importjavax.servlet.http.HttpServletRequest;importjava.text.SimpleDateFormat;importjava.util.Calendar;/** * 拉卡拉支付 Controller */@RestController@RequestMapping("/opmanager/pay/lakala")@Slf4j@Tag(name ="拉卡拉支付")publicclassLakalaPayControllerextendsBaseController{privatefinalSimpleDateFormat dateFormat =newSimpleDateFormat("yyyyMMddHHmmss");/** * 收银台订单创建(全报文加密) */@PostMapping("/counter/order/create/encry")@Operation(summary ="收银台订单创建(全报文加密)")publicAjaxResultcreateCounterOrderEncry(@RequestBodyCounterOrderCreateRequest request){try{// 如果前端传入了订单号,使用前端的;否则生成新的String orderNo = request.getOutOrderNo()!=null&&!request.getOutOrderNo().trim().isEmpty()? request.getOutOrderNo():generateOrderNo(); log.info("开始创建收银台订单(全报文加密),订单号:{},商户号:{},交易设备ID:{}", orderNo, request.getMerchantNo(), request.getVposId());// 构建请求对象V3CcssCounterOrderSpecialCreateEncryRequest lakalaRequest =newV3CcssCounterOrderSpecialCreateEncryRequest(); lakalaRequest.setOutOrderNo(orderNo); lakalaRequest.setMerchantNo(request.getMerchantNo()); lakalaRequest.setVposId(request.getVposId()); lakalaRequest.setChannelId(request.getChannelId()); lakalaRequest.setTotalAmount(request.getTotalAmount());// 设置订单有效期if(request.getOrderEfficientTime()!=null){ lakalaRequest.setOrderEfficientTime(request.getOrderEfficientTime());}else{// 默认7天有效期Calendar calendar =Calendar.getInstance(); calendar.add(Calendar.DAY_OF_MONTH,7); lakalaRequest.setOrderEfficientTime(dateFormat.format(calendar.getTime()));}// 设置回调地址 lakalaRequest.setNotifyUrl(request.getNotifyUrl()); lakalaRequest.setOrderInfo(request.getOrderInfo());// 调用LKLSDK创建订单String response = LKLSDK.httpPost(lakalaRequest,true,true); log.info("收银台订单创建完成,响应:{}", response);returnAjaxResult.success("订单创建成功", response);}catch(SDKException e){ log.error("收银台订单创建失败,SDK异常:{}", e.getMessage(), e);returnAjaxResult.error("订单创建失败:"+ e.getMessage());}catch(Exception e){ log.error("收银台订单创建失败,系统异常:{}", e.getMessage(), e);returnAjaxResult.error("订单创建失败:"+ e.getMessage());}}/** * 拉卡拉支付回调通知 */@PostMapping("/notify")@Operation(summary ="拉卡拉支付回调通知")publicStringpaymentNotify(HttpServletRequest request){try{ log.info("收到拉卡拉支付回调通知");// 验证回调签名String notifyBody = LKLSDK.notificationHandle(request); log.info("回调签名验证成功,通知内容:{}", notifyBody);// 解析回调数据PaymentNotifyData notifyData =JSONObject.parseObject(notifyBody,PaymentNotifyData.class); log.info("支付回调信息 - 商户订单号:{},支付订单号:{},支付状态:{},支付金额:{},支付时间:{}", notifyData.getOutOrderNo(), notifyData.getPayOrderNo(), notifyData.getPayStatus(), notifyData.getPayAmount(), notifyData.getPayTime());// 根据业务需求处理回调逻辑(需加订单级锁)if("SUCCESS".equals(notifyData.getPayStatus())){// 支付成功处理 log.info("支付成功,订单号:{},金额:{}", notifyData.getOutOrderNo(), notifyData.getPayAmount());// 这里可以调用业务服务更新订单状态// orderService.updateOrderStatus(notifyData.getOutOrderNo(), "PAID");}elseif("FAILED".equals(notifyData.getPayStatus())){// 支付失败处理 log.warn("支付失败,订单号:{}", notifyData.getOutOrderNo());}else{ log.warn("未知支付状态:{},订单号:{}", notifyData.getPayStatus(), notifyData.getOutOrderNo());}// 返回成功响应给拉卡拉服务器return"success";}catch(SDKException e){ log.error("回调签名验证失败:{}", e.getMessage(), e);return"fail";}catch(Exception e){ log.error("处理支付回调异常:{}", e.getMessage(), e);return"fail";}}/** * 生成订单号 */privateStringgenerateOrderNo(){return"LKL"+System.currentTimeMillis()+(int)(Math.random()*1000);}/** * 收银台订单创建请求DTO */@DatapublicstaticclassCounterOrderCreateRequest{privateString outOrderNo;// 商户订单号privateString merchantNo;// 银联商户号privateString vposId;// 交易设备标识privateString channelId;// 渠道号privateLong totalAmount;// 订单金额(单位:分)privateString orderEfficientTime;// 订单有效期privateString notifyUrl;// 回调地址privateString orderInfo;// 订单标题privateInteger supportCancel;// 是否支持撤销privateInteger supportRefund;// 是否支持退款privateInteger supportRepeatPay;// 是否支持多次支付privateString callbackUrl;// 前端跳转地址privateString counterParam;// 收银台参数privateString counterRemark;// 收银台备注}/** * 拉卡拉支付回调数据DTO */@DatapublicstaticclassPaymentNotifyData{privateString outOrderNo;// 商户订单号privateString payOrderNo;// 拉卡拉支付订单号privateString payStatus;// 支付状态 SUCCESS-成功 FAILED-失败privateString payAmount;// 支付金额(单位:分)privateString payTime;// 支付完成时间 yyyyMMddHHmmssprivateString merchantNo;// 商户号privateString vposId;// 交易设备标识privateString payChannel;// 支付渠道privateString payMethod;// 支付方式privateString attach;// 附加数据}}

回调处理

回调地址配置

在创建订单时,需要设置回调地址:

// 设置回调地址 lakalaRequest.setNotifyUrl("http://your-domain/opmanager/pay/lakala/notify");

回调数据格式

拉卡拉回调发送的JSON数据格式:

{"out_order_no":"LKL1703123456789","pay_order_no":"26011611012001101011002063626","pay_status":"SUCCESS","pay_amount":"1","pay_time":"20260116145443","merchant_no":"8222900701106PZ","vpos_id":"2021052614391","pay_channel":"ALIPAY","pay_method":"SCAN","attach":"附加数据"}

回调处理逻辑

  1. 签名验证:使用 LKLSDK.notificationHandle() 验证回调签名
  2. 数据解析:将JSON字符串解析为 PaymentNotifyData 对象
  3. 业务处理
    • 支付成功:更新订单状态为已支付
    • 支付失败:记录失败原因
    • 其他状态:记录日志并告警
  4. 响应处理:返回 “success” 告知拉卡拉处理成功

完整代码示例

1. 前端调用示例

// 创建支付订单constcreateOrder=async(orderData)=>{const response =awaitfetch('/opmanager/pay/lakala/counter/order/create/encry',{method:'POST',headers:{'Content-Type':'application/json',},body:JSON.stringify({merchantNo:'8222900701106PZ',vposId:'2021052614391',channelId:'2021052614391',totalAmount:100,// 1元 = 100分notifyUrl:'http://your-domain/opmanager/pay/lakala/notify',orderInfo:'商品购买'})});const result =await response.json();if(result.code ===200){// 获取支付链接并跳转const payUrl = result.data.counter_url; window.location.href = payUrl;}};

2. 回调处理业务逻辑示例

@ServicepublicclassOrderService{@AutowiredprivateOrderMapper orderMapper;/** * 更新订单支付状态 */@TransactionalpublicvoidupdateOrderStatus(String orderNo,String status){// 加订单级锁防止并发问题Order order = orderMapper.selectByOrderNoForUpdate(orderNo);if(order !=null&&"PENDING".equals(order.getStatus())){ order.setStatus(status); order.setPayTime(newDate()); orderMapper.updateById(order);// 发送支付成功通知sendPaymentSuccessNotification(order);}}}

注意事项

1. 安全配置

  • ✅ 私钥证书妥善保管,不要在日志中输出
  • ✅ 使用HTTPS协议传输敏感数据
  • ✅ 定期更新证书,避免过期

2. 订单处理

  • ✅ 实现幂等性,避免重复处理同一订单
  • ✅ 添加订单级锁,防止并发更新问题
  • ✅ 记录详细的支付流水日志

3. 异常处理

  • ✅ 妥善处理网络异常和超时
  • ✅ 实现重试机制,但避免无限重试
  • ✅ 监控支付成功率和异常情况

4. 回调处理

  • ✅ 回调接口必须返回 “success”,否则拉卡拉会重试
  • ✅ 回调处理要快速响应,避免超时
  • ✅ 验证回调数据的完整性和正确性

常见问题

Q1: SDK初始化失败怎么处理?

A: 检查配置文件中的证书格式是否正确,私钥是否匹配,网络连接是否正常。

Q2: 订单创建失败的原因?

A: 可能是参数格式错误、金额超出限制、商户状态异常等。检查返回的错误信息。

Q3: 回调没有收到怎么办?

A: 检查回调地址是否正确可访问,防火墙是否阻止了请求,服务器是否正常运行。

Q4: 如何处理重复回调?

A: 在业务逻辑中检查订单状态,只有在特定状态下才处理回调,避免重复处理。

Q5: 支付状态不一致怎么办?

A: 以拉卡拉回调为准,主动查询订单状态进行校对,记录异常情况并告警。


本文档提供完整的拉卡拉支付集成方案,如有问题请参考官方文档或联系技术支持。

Read more

Flutter 三方库 wasm_ffi 深入鸿蒙端侧硬核 WebAssembly 虚拟机沙盒穿透适配全景:通过异步极速 FFI 中继管道打通底层高算力异构服务-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 wasm_ffi 深入鸿蒙端侧硬核 WebAssembly 虚拟机沙盒穿透适配全景:通过异步极速 FFI 中继管道打通底层高算力异构服务-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 wasm_ffi 深入鸿蒙端侧硬核 WebAssembly 虚拟机沙盒穿透适配全景:通过异步极速 FFI 中继管道打通底层高算力异构服务并全面实现无损语言壁垒交互 前言 在 OpenHarmony 应用向高性能计算领域扩展的过程中,如何优雅地接入已有的 C/C++ 算法库(如加密引擎、重型图像处理、数学模拟)而又不失跨平台的便捷性?传统的 NAPI 虽然稳健,但在 Flutter 生态中,直接利用 WebAssembly (WASM) 配合 FFI(External Function Interface)的语义可以在一定程度上实现代码的高度复用。wasm_ffi 库为 Flutter 开发者提供了一套在 Dart 环境下调用 WASM

By Ne0inhk
三种适用于Web版IM(即时通讯)聊天信息的加密算法实现方案

三种适用于Web版IM(即时通讯)聊天信息的加密算法实现方案

文章目录 * **第一部分:引言与核心密码学概念** * **1.1 为什么IM需要端到端加密(E2EE)?** * **1.2 核心密码学概念与工具** * **第二部分:方案一:静态非对称加密(基础方案)** * **2.1 方案概述与流程** * **2.2 前端Vue实现(使用node-forge)** * **1. 安装依赖** * **2. 核心工具类 `crypto.js`** * **3. Vue组件中使用** * **2.3 后端Java实现(Spring Boot)** * **1. 实体类** * **2. Controller层** * **3. WebSocket配置** * **2.4 密钥管理、注册与登录集成** * **1. 用户注册/登录时生成密钥** * **2. 密钥设置页面** * **2.

By Ne0inhk
前端代码生成的大洗牌:当 GLM 4.7 与 MiniMax 挑战 Claude Opus,谁才是性价比之王?

前端代码生成的大洗牌:当 GLM 4.7 与 MiniMax 挑战 Claude Opus,谁才是性价比之王?

在 AI 辅助编程领域,长期以来似乎存在一条不成文的铁律:如果你想要最好的结果,就必须为最昂贵的模型买单(通常是 Anthropic 或 OpenAI 的旗舰模型)。然而,随着国产大模型如 GLM 4.7 和 MiniMax M2.1 的迭代,这一格局正在发生剧烈震荡。 最近,一场针对Claude Opus 4.5、Gemini 3 Pro、GLM 4.7 和 MiniMax M2.1 的前端 UI生成横向测评,打破了许多人的固有认知。在这场包含落地页、仪表盘、移动端应用等五个真实场景的较量中,不仅出现了令人咋舌的“滑铁卢”,更诞生了性价比极高的“新王”。 本文将深入拆解这场测试的细节,透过代码生成的表象,探讨大模型在工程化落地中的真实效能与成本逻辑。

By Ne0inhk
【Java Web学习 | 第14篇】JavaScript(8) -正则表达式

【Java Web学习 | 第14篇】JavaScript(8) -正则表达式

🌈个人主页: Hygge_Code🔥热门专栏:从0开始学习Java | Linux学习| 计算机网络💫个人格言: “既然选择了远方,便不顾风雨兼程” 文章目录 * JavaScript 正则表达式详解 * 什么是正则表达式🤔 * JavaScript 正则表达式的定义与使用🥝 * 1. 字面量语法 * 2. 常用匹配方法 * test() 方法🍋‍🟩 * exec() 方法🍋‍🟩 * 正则表达式的核心组成部分🐦‍🔥 * 1. 元字符 * 边界符 * 量词 * 字符类 * 2. 修饰符 * 简单示例🍂 JavaScript 正则表达式详解 正则表达式是处理字符串的强大工具,在 JavaScript 中被广泛应用于表单验证、文本处理和数据提取等场景。本文将从正则表达式的基本概念出发,详细介绍其语法规则和实际应用方法。 什么是正则表达式🤔 正则表达式是用于匹配字符串中字符组合的模式,在 JavaScript

By Ne0inhk