最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java對(duì)接拉卡拉支付完整指南(附詳細(xì)代碼)

 更新時(shí)間:2026年03月26日 09:00:39   作者:crispsea  
拉卡拉支付SDK是一個(gè)面向Java開(kāi)發(fā)者的支付解決方案,其設(shè)計(jì)目的是為了幫助開(kāi)發(fā)者在Java環(huán)境中輕松集成拉卡拉的支付功能,這篇文章主要介紹了Java對(duì)接拉卡拉支付的相關(guān)資料,需要的朋友可以參考下

概述

拉卡拉支付是中國(guó)領(lǐng)先的第三方支付解決方案提供商。本文檔詳細(xì)介紹如何在Java Spring Boot項(xiàng)目中集成拉卡拉支付SDK,實(shí)現(xiàn)收銀臺(tái)訂單創(chuàng)建和支付回調(diào)處理功能。

功能特性

  • ? 收銀臺(tái)訂單創(chuàng)建(全報(bào)文加密)
  • ? 支付回調(diào)通知處理
  • ? 簽名驗(yàn)證
  • ? 訂單狀態(tài)管理

1.環(huán)境準(zhǔn)備

接入官方sdk

  <!--         拉卡拉支付依賴(lài)-->
        <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>

導(dǎo)入依賴(lài) 然后根據(jù)sdk寫(xiě)文檔拉起支付

2. 證書(shū)準(zhǔn)備

需要從拉卡拉官方獲取以下證書(shū)文件:

  • 商戶(hù)私鑰證書(shū)(PEM格式)
  • 拉卡拉平臺(tái)證書(shū)(PEM格式)
  • 拉卡拉平臺(tái)通知證書(shū)(PEM格式)

SDK初始化配置

1. 配置文件 (application.yml)

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

# 拉卡拉支付SDK配置
lakala:
  sdk:
    # 拉卡拉應(yīng)用ID
    app-id: "OP00000003"
    # 商戶(hù)證書(shū)序列號(hào)
    serial-no: "dfba8194c41b84cf"
    # 商戶(hù)私鑰(PEM格式字符串)
    private-key: |
      -----BEGIN PRIVATE KEY-----
      MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDvDBZyHUDndAGx
      rIcsCV2njhNO3vCEZotTaWYSYwtDvkcAb1EjsBFabXZaKigpqFXk5XXNI3NIHP9M
      ...(私鑰內(nèi)容)
      -----END PRIVATE KEY-----
    # 拉卡拉平臺(tái)證書(shū)(用于驗(yàn)證API響應(yīng)簽名)
    lkl-certificate: |
      -----BEGIN CERTIFICATE-----
      MIIEMTCCAxmgAwIBAgIGAXRTgcMnMA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYT
      ...(平臺(tái)證書(shū)內(nèi)容)
      -----END CERTIFICATE-----
    # 拉卡拉平臺(tái)通知證書(shū)(用于驗(yàn)證異步通知簽名)
    lkl-notify-certificate: |
      -----BEGIN CERTIFICATE-----
      MIIEMTCCAxmgAwIBAgIGAXRTgcMnMA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYT
      ...(通知證書(shū)內(nèi)容)
      -----END CERTIFICATE-----
    # SM4加密密鑰(Base64編碼)
    sm4-key: "LHo55AjrT4aDhAIBZhb5KQ=="
    # API服務(wù)器地址
    server-url: "https://test.wsmsd.cn/sit"
    # 連接超時(shí)時(shí)間(毫秒)
    connect-timeout: 50000
    # 讀取超時(shí)時(shí)間(毫秒)
    read-timeout: 100000
    # Socket超時(shí)時(shí)間(毫秒)
    socket-timeout: 50000

2. SDK初始化類(lèi) (LakalaSdkConfig.java)

創(chuàng)建SDK初始化配置類(lèi):

package com.shunp.framework.config;
import com.laop.sdk.Config2;
import com.laop.sdk.LKLSDK;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
 * 拉卡拉SDK配置類(lèi)
 */
@Configuration
@Slf4j
public class LakalaSdkConfig {
    @Value("${lakala.sdk.app-id:}")
    private String appId;
    @Value("${lakala.sdk.serial-no:}")
    private String serialNo;
    @Value("${lakala.sdk.private-key:}")
    private String privateKey;
    @Value("${lakala.sdk.lkl-certificate:}")
    private String lklCertificate;
    @Value("${lakala.sdk.lkl-notify-certificate:}")
    private String lklNotifyCertificate;
    @Value("${lakala.sdk.sm4-key:}")
    private String sm4Key;
    @Value("${lakala.sdk.server-url:}")
    private String serverUrl;
    @Value("${lakala.sdk.connect-timeout:50000}")
    private Integer connectTimeout;
    @Value("${lakala.sdk.read-timeout:100000}")
    private Integer readTimeout;
    @Value("${lakala.sdk.socket-timeout:50000}")
    private Integer socketTimeout;
    /**
     * 初始化LKLSDK
     */
    @PostConstruct
    public void initLklSdk() {
        try {
            log.info("開(kāi)始初始化拉卡拉SDK...");
            // 校驗(yàn)必要參數(shù)
            if (appId == null || appId.trim().isEmpty()) {
                log.warn("拉卡拉SDK appId未配置,跳過(guò)初始化");
                return;
            }
            // ... 其他參數(shù)校驗(yàn)
            // 創(chuàng)建Config2對(duì)象
            Config2 config = new Config2();
            config.setAppId(appId.trim());
            config.setSerialNo(serialNo.trim());
            config.setPriKey(privateKey.trim());
            config.setLklCer(lklCertificate.trim());
            config.setLklNotifyCer(lklNotifyCertificate.trim());
            config.setServerUrl(serverUrl.trim());
            // 可選參數(shù)
            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);
            }
            // 初始化SDK
            boolean initResult = LKLSDK.init(config);
            if (initResult) {
                log.info("拉卡拉SDK初始化成功,appId: {}", appId);
            } else {
                log.error("拉卡拉SDK初始化失敗,appId: {}", appId);
            }
        } catch (Exception e) {
            log.error("拉卡拉SDK初始化異常", e);
        }
    }
}

支付接口實(shí)現(xiàn)

1. 支付Controller (LakalaPayController.java)

package com.shunp.web.controller.pay;
import com.alibaba.fastjson2.JSONObject;
import com.laop.sdk.LKLSDK;
import com.laop.sdk.exception.SDKException;
import com.laop.sdk.request.V3CcssCounterOrderSpecialCreateEncryRequest;
import com.shunp.common.core.controller.BaseController;
import com.shunp.common.core.domain.AjaxResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Calendar;
/**
 * 拉卡拉支付 Controller
 */
@RestController
@RequestMapping("/opmanager/pay/lakala")
@Slf4j
@Tag(name = "拉卡拉支付")
public class LakalaPayController extends BaseController {
    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    /**
     * 收銀臺(tái)訂單創(chuàng)建(全報(bào)文加密)
     */
    @PostMapping("/counter/order/create/encry")
    @Operation(summary = "收銀臺(tái)訂單創(chuàng)建(全報(bào)文加密)")
    public AjaxResult createCounterOrderEncry(@RequestBody CounterOrderCreateRequest request) {
        try {
            // 如果前端傳入了訂單號(hào),使用前端的;否則生成新的
            String orderNo = request.getOutOrderNo() != null && !request.getOutOrderNo().trim().isEmpty()
                    ? request.getOutOrderNo()
                    : generateOrderNo();
            log.info("開(kāi)始創(chuàng)建收銀臺(tái)訂單(全報(bào)文加密),訂單號(hào):{},商戶(hù)號(hào):{},交易設(shè)備ID:{}",
                    orderNo, request.getMerchantNo(), request.getVposId());
            // 構(gòu)建請(qǐng)求對(duì)象
            V3CcssCounterOrderSpecialCreateEncryRequest lakalaRequest =
                new V3CcssCounterOrderSpecialCreateEncryRequest();
            lakalaRequest.setOutOrderNo(orderNo);
            lakalaRequest.setMerchantNo(request.getMerchantNo());
            lakalaRequest.setVposId(request.getVposId());
            lakalaRequest.setChannelId(request.getChannelId());
            lakalaRequest.setTotalAmount(request.getTotalAmount());
            // 設(shè)置訂單有效期
            if (request.getOrderEfficientTime() != null) {
                lakalaRequest.setOrderEfficientTime(request.getOrderEfficientTime());
            } else {
                // 默認(rèn)7天有效期
                Calendar calendar = Calendar.getInstance();
                calendar.add(Calendar.DAY_OF_MONTH, 7);
                lakalaRequest.setOrderEfficientTime(dateFormat.format(calendar.getTime()));
            }
            // 設(shè)置回調(diào)地址
            lakalaRequest.setNotifyUrl(request.getNotifyUrl());
            lakalaRequest.setOrderInfo(request.getOrderInfo());
            // 調(diào)用LKLSDK創(chuàng)建訂單
            String response = LKLSDK.httpPost(lakalaRequest, true, true);
            log.info("收銀臺(tái)訂單創(chuàng)建完成,響應(yīng):{}", response);
            return AjaxResult.success("訂單創(chuàng)建成功", response);
        } catch (SDKException e) {
            log.error("收銀臺(tái)訂單創(chuàng)建失敗,SDK異常:{}", e.getMessage(), e);
            return AjaxResult.error("訂單創(chuàng)建失?。? + e.getMessage());
        } catch (Exception e) {
            log.error("收銀臺(tái)訂單創(chuàng)建失敗,系統(tǒng)異常:{}", e.getMessage(), e);
            return AjaxResult.error("訂單創(chuàng)建失?。? + e.getMessage());
        }
    }
    /**
     * 拉卡拉支付回調(diào)通知
     */
    @PostMapping("/notify")
    @Operation(summary = "拉卡拉支付回調(diào)通知")
    public String paymentNotify(HttpServletRequest request) {
        try {
            log.info("收到拉卡拉支付回調(diào)通知");
            // 驗(yàn)證回調(diào)簽名
            String notifyBody = LKLSDK.notificationHandle(request);
            log.info("回調(diào)簽名驗(yàn)證成功,通知內(nèi)容:{}", notifyBody);
            // 解析回調(diào)數(shù)據(jù)
            PaymentNotifyData notifyData = JSONObject.parseObject(notifyBody, PaymentNotifyData.class);
            log.info("支付回調(diào)信息 - 商戶(hù)訂單號(hào):{},支付訂單號(hào):{},支付狀態(tài):{},支付金額:{},支付時(shí)間:{}",
                    notifyData.getOutOrderNo(), notifyData.getPayOrderNo(),
                    notifyData.getPayStatus(), notifyData.getPayAmount(), notifyData.getPayTime());
            // 根據(jù)業(yè)務(wù)需求處理回調(diào)邏輯(需加訂單級(jí)鎖)
            if ("SUCCESS".equals(notifyData.getPayStatus())) {
                // 支付成功處理
                log.info("支付成功,訂單號(hào):{},金額:{}", notifyData.getOutOrderNo(), notifyData.getPayAmount());
                // 這里可以調(diào)用業(yè)務(wù)服務(wù)更新訂單狀態(tài)
                // orderService.updateOrderStatus(notifyData.getOutOrderNo(), "PAID");
            } else if ("FAILED".equals(notifyData.getPayStatus())) {
                // 支付失敗處理
                log.warn("支付失敗,訂單號(hào):{}", notifyData.getOutOrderNo());
            } else {
                log.warn("未知支付狀態(tài):{},訂單號(hào):{}", notifyData.getPayStatus(), notifyData.getOutOrderNo());
            }
            // 返回成功響應(yīng)給拉卡拉服務(wù)器
            return "success";
        } catch (SDKException e) {
            log.error("回調(diào)簽名驗(yàn)證失?。簕}", e.getMessage(), e);
            return "fail";
        } catch (Exception e) {
            log.error("處理支付回調(diào)異常:{}", e.getMessage(), e);
            return "fail";
        }
    }
    /**
     * 生成訂單號(hào)
     */
    private String generateOrderNo() {
        return "LKL" + System.currentTimeMillis() + (int)(Math.random() * 1000);
    }
    /**
     * 收銀臺(tái)訂單創(chuàng)建請(qǐng)求DTO
     */
    @Data
    public static class CounterOrderCreateRequest {
        private String outOrderNo;           // 商戶(hù)訂單號(hào)
        private String merchantNo;           // 銀聯(lián)商戶(hù)號(hào)
        private String vposId;               // 交易設(shè)備標(biāo)識(shí)
        private String channelId;            // 渠道號(hào)
        private Long totalAmount;            // 訂單金額(單位:分)
        private String orderEfficientTime;   // 訂單有效期
        private String notifyUrl;            // 回調(diào)地址
        private String orderInfo;            // 訂單標(biāo)題
        private Integer supportCancel;       // 是否支持撤銷(xiāo)
        private Integer supportRefund;       // 是否支持退款
        private Integer supportRepeatPay;    // 是否支持多次支付
        private String callbackUrl;          // 前端跳轉(zhuǎn)地址
        private String counterParam;         // 收銀臺(tái)參數(shù)
        private String counterRemark;        // 收銀臺(tái)備注
    }
    /**
     * 拉卡拉支付回調(diào)數(shù)據(jù)DTO
     */
    @Data
    public static class PaymentNotifyData {
        private String outOrderNo;       // 商戶(hù)訂單號(hào)
        private String payOrderNo;       // 拉卡拉支付訂單號(hào)
        private String payStatus;        // 支付狀態(tài) SUCCESS-成功 FAILED-失敗
        private String payAmount;        // 支付金額(單位:分)
        private String payTime;          // 支付完成時(shí)間 yyyyMMddHHmmss
        private String merchantNo;       // 商戶(hù)號(hào)
        private String vposId;           // 交易設(shè)備標(biāo)識(shí)
        private String payChannel;       // 支付渠道
        private String payMethod;        // 支付方式
        private String attach;           // 附加數(shù)據(jù)
    }
}

回調(diào)處理

回調(diào)地址配置

在創(chuàng)建訂單時(shí),需要設(shè)置回調(diào)地址:

// 設(shè)置回調(diào)地址
lakalaRequest.setNotifyUrl("http://your-domain/opmanager/pay/lakala/notify");

回調(diào)數(shù)據(jù)格式

拉卡拉回調(diào)發(fā)送的JSON數(shù)據(jù)格式:

{
    "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": "附加數(shù)據(jù)"
}

回調(diào)處理邏輯

  1. 簽名驗(yàn)證:使用 LKLSDK.notificationHandle() 驗(yàn)證回調(diào)簽名
  2. 數(shù)據(jù)解析:將JSON字符串解析為 PaymentNotifyData 對(duì)象
  3. 業(yè)務(wù)處理
    • 支付成功:更新訂單狀態(tài)為已支付
    • 支付失敗:記錄失敗原因
    • 其他狀態(tài):記錄日志并告警
  4. 響應(yīng)處理:返回 “success” 告知拉卡拉處理成功

完整代碼示例

1. 前端調(diào)用示例

// 創(chuàng)建支付訂單
const createOrder = async (orderData) => {
    const response = await fetch('/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: '商品購(gòu)買(mǎi)'
        })
    });
    const result = await response.json();
    if (result.code === 200) {
        // 獲取支付鏈接并跳轉(zhuǎn)
        const payUrl = result.data.counter_url;
        window.location.href = payUrl;
    }
};

2. 回調(diào)處理業(yè)務(wù)邏輯示例

@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    /**
     * 更新訂單支付狀態(tài)
     */
    @Transactional
    public void updateOrderStatus(String orderNo, String status) {
        // 加訂單級(jí)鎖防止并發(fā)問(wèn)題
        Order order = orderMapper.selectByOrderNoForUpdate(orderNo);
        if (order != null && "PENDING".equals(order.getStatus())) {
            order.setStatus(status);
            order.setPayTime(new Date());
            orderMapper.updateById(order);
            // 發(fā)送支付成功通知
            sendPaymentSuccessNotification(order);
        }
    }
}

注意事項(xiàng)

1. 安全配置

  • ? 私鑰證書(shū)妥善保管,不要在日志中輸出
  • ? 使用HTTPS協(xié)議傳輸敏感數(shù)據(jù)
  • ? 定期更新證書(shū),避免過(guò)期

2. 訂單處理

  • ? 實(shí)現(xiàn)冪等性,避免重復(fù)處理同一訂單
  • ? 添加訂單級(jí)鎖,防止并發(fā)更新問(wèn)題
  • ? 記錄詳細(xì)的支付流水日志

3. 異常處理

  • ? 妥善處理網(wǎng)絡(luò)異常和超時(shí)
  • ? 實(shí)現(xiàn)重試機(jī)制,但避免無(wú)限重試
  • ? 監(jiān)控支付成功率和異常情況

4. 回調(diào)處理

  • ? 回調(diào)接口必須返回 “success”,否則拉卡拉會(huì)重試
  • ? 回調(diào)處理要快速響應(yīng),避免超時(shí)
  • ? 驗(yàn)證回調(diào)數(shù)據(jù)的完整性和正確性

常見(jiàn)問(wèn)題

Q1: SDK初始化失敗怎么處理?

A: 檢查配置文件中的證書(shū)格式是否正確,私鑰是否匹配,網(wǎng)絡(luò)連接是否正常。

Q2: 訂單創(chuàng)建失敗的原因?

A: 可能是參數(shù)格式錯(cuò)誤、金額超出限制、商戶(hù)狀態(tài)異常等。檢查返回的錯(cuò)誤信息。

Q3: 回調(diào)沒(méi)有收到怎么辦?

A: 檢查回調(diào)地址是否正確可訪問(wèn),防火墻是否阻止了請(qǐng)求,服務(wù)器是否正常運(yùn)行。

Q4: 如何處理重復(fù)回調(diào)?

A: 在業(yè)務(wù)邏輯中檢查訂單狀態(tài),只有在特定狀態(tài)下才處理回調(diào),避免重復(fù)處理。

Q5: 支付狀態(tài)不一致怎么辦?

A: 以拉卡拉回調(diào)為準(zhǔn),主動(dòng)查詢(xún)訂單狀態(tài)進(jìn)行校對(duì),記錄異常情況并告警。

本文檔提供完整的拉卡拉支付集成方案,如有問(wèn)題請(qǐng)參考官方文檔或聯(lián)系技術(shù)支持。

總結(jié)

到此這篇關(guān)于Java對(duì)接拉卡拉支付完整指南的文章就介紹到這了,更多相關(guān)Java對(duì)接拉卡拉支付內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

许昌县| 雷州市| 晴隆县| 滦平县| 明光市| 汝州市| 罗山县| 扶绥县| 乐业县| 佛冈县| 即墨市| 肇源县| 贡山| 仁布县| 普兰店市| 新建县| 黑山县| 靖边县| 安西县| 勐海县| 大渡口区| 郧西县| 满城县| 密云县| 重庆市| 古蔺县| 霍林郭勒市| 白朗县| 扶余县| 邵阳市| 桃园市| 伊金霍洛旗| 平泉县| 垫江县| 甘孜| 武宁县| 关岭| 营口市| 惠来县| 巧家县| 文成县|