package com.ruoyi.web.controller.api; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.lkl.laop.sdk.exception.SDKException; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.framework.web.service.TokenService; import com.ruoyi.system.model.TCrmSupplier; import com.ruoyi.system.model.TErpProcurement; import com.ruoyi.system.model.TErpProcurementGoods; import com.ruoyi.system.query.TErpGoodsWarehouseQuery; import com.ruoyi.system.service.*; import com.ruoyi.web.core.config.LakalaConfig; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.SneakyThrows; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.poi.ss.formula.functions.T; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.io.*; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.cert.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadPoolExecutor; import java.util.stream.Collectors; /** *

* 订单管理 前端控制器 *

* * @author xiaochen * @since 2025-08-20 */ @RestController @RequestMapping("/t-sys-order") @Api(tags = "订单管理") public class TSysOrderController { private final TErpProcurementService erpProcurementService; private final TErpProcurementGoodsService erpProcurementGoodsService; private final TCrmSupplierService crmSupplierService; private final TokenService tokenService; @Autowired public TSysOrderController(TErpProcurementService erpProcurementService, TokenService tokenService, TErpProcurementGoodsService erpProcurementGoodsService, TCrmSupplierService crmSupplierService) { this.erpProcurementService = erpProcurementService; this.tokenService = tokenService; this.erpProcurementGoodsService = erpProcurementGoodsService; this.crmSupplierService = crmSupplierService; } private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; /** * API schema ,固定 LKLAPI-SHA256withRSA */ public final static String SCHEMA = "LKLAPI-SHA256withRSA"; /** * 字符集固定 utf-8 */ public static final String ENCODING = "utf-8"; private static final SecureRandom RANDOM = new SecureRandom(); @ApiOperation(value = "支付") @PostMapping(value = "/payOrder") @SneakyThrows public R payOrder(@RequestParam String id, String type, HttpServletRequest request) { SysUser user = tokenService.getLoginUser().getUser(); try { // 查出采购单 算出价格 下单 算出应该分佣金额 应该分给谁 TErpProcurement erpProcurement = erpProcurementService.getById(id); String ipAddr = IpUtils.getIpAddr(request); String time = DateUtils.dateTimeNow(); // SYMBOLS 随机取4位数 String nonce = ""; for (int i = 0; i < 6; i++) { nonce += SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length())); } String outTradeNo = time + nonce; String amount = String.valueOf(erpProcurement.getPayMoney().multiply(new BigDecimal("100")).intValue()); String apiPath = "sit/api/v3/labs/trans/preorder"; String body = "{\n" + "\t\"req_time\": \"" + time + "\",\n" + "\t\"version\": \"3.0\",\n" + "\t\"req_data\": {\n" + "\t\t\"out_trade_no\": \"" + outTradeNo + "\",\n" + "\t\t\"merchant_no\": \"822290059430BFA\",\n" + "\t\t\"term_no\": \"D9261078\",\n" + "\t\t\"notify_url\": \"http://221.182.45.100:8089/t-sys-order/messageHandle\",\n" + "\t\t\"location_info\": {\n" + "\t\t\t\"request_ip\": \"" + ipAddr + "\"\n" + "\t\t},\n" + "\t\t\"subject\": \"商品采购\",\n" + "\t\t\"settle_type\": \"1\",\n" + "\t\t\"total_amount\": " + amount + ",\n" + "\t\t\"account_type\": \"" + type + "\",\n" + "\t\t\"trans_type\": \"41\"\n" + "\t}\n" + "}"; String authorization = getAuthorization(body); org.apache.http.HttpResponse response = post(LakalaConfig.getServerUrl() + apiPath, body, authorization); if (response.getStatusLine().getStatusCode() != 200) { return R.fail(500, "请求失败,statusCode " + response.getStatusLine() + IOUtils.toString(response.getEntity().getContent(), ENCODING)); } String responseStr = IOUtils.toString(response.getEntity().getContent(), ENCODING); erpProcurement.setPayNumber(outTradeNo); JSONObject jsonObject = JSONObject.parseObject(responseStr); JSONObject jsonObject1 = jsonObject.getJSONObject("resp_data"); String code = jsonObject1.getString("log_no"); erpProcurement.setPayTransactionId(code); erpProcurement.setStatus(2); erpProcurement.setTermNo("D9261078"); erpProcurement.setAccountType(type); erpProcurement.setTransType("41"); erpProcurementService.updateById(erpProcurement); return R.ok(responseStr); } catch (SDKException e) { e.printStackTrace(); } return R.fail(500, "调用支付宝支付预下单接口错误"); } public static void main(String[] args) { String a = "{\"code\":\"BBS00000\",\"msg\":\"成功\",\"resp_time\":\"20250916114506\",\"resp_data\":{\"merchant_no\":\"822290059430BFA\",\"out_trade_no\":\"202509161144582Kck\",\"trade_no\":\"2025091666200821320234\",\"log_no\":\"66200821320234\",\"settle_merchant_no\":\"\",\"settle_term_no\":\"\",\"trade_req_date\":\"20250916\",\"acc_resp_fields\":{\"code\":\"https://qr.alipay.com/bax01747cbtdjneesjaz0044\",\"code_image\":\"\",\"prepay_id\":\"\",\"app_id\":\"\",\"pay_sign\":\"\",\"time_stamp\":\"\",\"nonce_str\":\"\",\"package\":\"\",\"sign_type\":\"\",\"form_data\":\"\",\"redirect_url\":\"\",\"best_pay_info\":\"\",\"partner_id\":\"\",\"sub_mch_id\":\"2088240727800097\"}}}"; } @RequestMapping("/messageHandle") @ApiOperation(value = "拉卡拉支付信息回调接口") public Object messageHandle(HttpServletRequest request) throws Exception { String body = this.getBody(request); String authorization = request.getHeader("Authorization"); String timestamp = null; String nonce = null; String signature = null; authorization = authorization.replaceAll("LKLAPI-SHA256withRSA ", ""); String[] split = authorization.split(","); for (String s : split) { if (s.startsWith("timestamp")) { timestamp = s.split("=")[1].replaceAll("\"", ""); } if (s.startsWith("nonce")) { nonce = s.split("=")[1].replaceAll("\"", ""); } if (s.startsWith("signature")) { // 取出签名 signature = s.split("signature=")[1].replaceAll("\"", ""); } } X509Certificate lklCertificate = loadCertificate(new FileInputStream(new File(LakalaConfig.getLklNotifyCerStr()))); String preSignData = timestamp + "\n" + nonce + "\n" + body + "\n"; boolean verify = verify(lklCertificate, preSignData.getBytes(ENCODING), signature); // 先处理支付完成 修改状态 后处理分账 后走分账回调 JSONObject jsonObject1 = JSONObject.parseObject(body); Object o = jsonObject1.get("out_trade_no"); TErpProcurement erpProcurement = erpProcurementService.getOne(new LambdaQueryWrapper().eq(TErpProcurement::getPayNumber, o)); if (verify && erpProcurement.getStatus()==2) { erpProcurement.setPayTime(LocalDateTime.now()); erpProcurement.setStatus(3); erpProcurementService.updateById(erpProcurement); List list = erpProcurementGoodsService.list(new LambdaQueryWrapper().eq(TErpProcurementGoods::getProcurementId, erpProcurement.getId())); // 操作分账 HttpRequest post = HttpUtil.createPost(LakalaConfig.getServerUrl() + "sit/api/v3/sacs/separate"); HashMap map = new HashMap<>(); map.put("merchant_no", "822290059430BFA"); map.put("log_no", erpProcurement.getPayTransactionId()); map.put("log_date", DateUtils.dateTime()); map.put("notify_url", "http://221.182.45.100:8089/t-sys-order/messageSeparateHandle"); String time = DateUtils.dateTimeNow(); // SYMBOLS 随机取4位数 String str = ""; for (int i = 0; i < 6; i++) { str += SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length())); } String outTradeNo = time + str; map.put("out_separate_no", outTradeNo); map.put("total_amt", erpProcurement.getPayMoney().multiply(BigDecimal.valueOf(100)).intValue() + ""); // 平台应得分账 BigDecimal payMoney = erpProcurement.getPayMoney(); ArrayList> objects = new ArrayList<>(); // 根据供应商id分组 Map> collect = list.stream().collect(Collectors.groupingBy(TErpProcurementGoods::getSupplierId)); for (Map.Entry> entry : collect.entrySet()) { HashMap map1 = new HashMap<>(); String supplierId = entry.getKey(); TCrmSupplier supplier = crmSupplierService.getById(supplierId); List value = entry.getValue(); BigDecimal reduce = value.stream().map(TErpProcurementGoods::getSupplierMoney).reduce(BigDecimal.ZERO, BigDecimal::add); payMoney = payMoney.subtract(reduce); map1.put("recv_merchant_no", supplier.getRecvMerchantNo()); map1.put("separate_value", reduce.multiply(BigDecimal.valueOf(100)).intValue() + ""); objects.add(map1); } // 添加平台的 map.put("recv_datas", objects); String jsonString = JSON.toJSONString(map); post.body(jsonString); HttpResponse response = post.execute(); String responseStr = response.body(); JSONObject jsonObject = JSONObject.parseObject(responseStr); if ("成功".equals(jsonObject.getString("msg"))) { Object o1 = jsonObject.get("resp_data"); JSONObject jsonObject2 = JSONObject.parseObject(o1.toString()); String separate_no = jsonObject2.getString("separate_no"); erpProcurement.setSeparateNo(separate_no); erpProcurementService.updateById(erpProcurement); } // 响应success JSONObject responseJsonObject = new JSONObject(); responseJsonObject.put("code", "SUCCESS"); responseJsonObject.put("message", "执行成功"); return responseJsonObject; } return null; } @RequestMapping("/messageSeparateHandle") @ApiOperation(value = "拉卡拉分账信息回调接口") public Object messageSeparateHandle(HttpServletRequest request) throws Exception { String body = this.getBody(request); String authorization = request.getHeader("Authorization"); String timestamp = null; String nonce = null; String signature = null; authorization = authorization.replaceAll("LKLAPI-SHA256withRSA ", ""); String[] split = authorization.split(","); for (String s : split) { if (s.startsWith("timestamp")) { timestamp = s.split("=")[1].replaceAll("\"", ""); } if (s.startsWith("nonce")) { nonce = s.split("=")[1].replaceAll("\"", ""); } if (s.startsWith("signature")) { // 取出签名 signature = s.split("signature=")[1].replaceAll("\"", ""); } } X509Certificate lklCertificate = loadCertificate(new FileInputStream(new File(LakalaConfig.getLklNotifyCerStr()))); String preSignData = timestamp + "\n" + nonce + "\n" + body + "\n"; boolean verify = verify(lklCertificate, preSignData.getBytes(ENCODING), signature); // 先处理支付完成 修改状态 后处理分账 后走分账回调 JSONObject jsonObject1 = JSONObject.parseObject(body); Object o = jsonObject1.get("out_trade_no"); TErpProcurement erpProcurement = erpProcurementService.getOne(new LambdaQueryWrapper().eq(TErpProcurement::getPayNumber, o)); if (verify && erpProcurement.getStatus()==2) { erpProcurement.setPayTime(LocalDateTime.now()); erpProcurement.setStatus(3); erpProcurementService.updateById(erpProcurement); List list = erpProcurementGoodsService.list(new LambdaQueryWrapper().eq(TErpProcurementGoods::getProcurementId, erpProcurement.getId())); // 操作分账 HttpRequest post = HttpUtil.createPost(LakalaConfig.getServerUrl() + "sit/api/v3/sacs/separate"); HashMap map = new HashMap<>(); map.put("merchant_no", "822290059430BFA"); map.put("log_no", erpProcurement.getPayTransactionId()); map.put("log_date", DateUtils.dateTime()); map.put("notify_url", DateUtils.dateTime()); String time = DateUtils.dateTimeNow(); // SYMBOLS 随机取4位数 String str = ""; for (int i = 0; i < 6; i++) { str += SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length())); } String outTradeNo = time + str; map.put("out_separate_no", outTradeNo); map.put("total_amt", erpProcurement.getPayMoney().multiply(BigDecimal.valueOf(100)).intValue() + ""); // 平台应得分账 BigDecimal payMoney = erpProcurement.getPayMoney(); ArrayList> objects = new ArrayList<>(); // 根据供应商id分组 Map> collect = list.stream().collect(Collectors.groupingBy(TErpProcurementGoods::getSupplierId)); for (Map.Entry> entry : collect.entrySet()) { HashMap map1 = new HashMap<>(); String supplierId = entry.getKey(); TCrmSupplier supplier = crmSupplierService.getById(supplierId); List value = entry.getValue(); BigDecimal reduce = value.stream().map(TErpProcurementGoods::getSupplierMoney).reduce(BigDecimal.ZERO, BigDecimal::add); payMoney = payMoney.subtract(reduce); map1.put("recv_merchant_no", supplier.getRecvMerchantNo()); map1.put("separate_value", reduce.multiply(BigDecimal.valueOf(100)).intValue() + ""); objects.add(map1); } // 添加平台的 map.put("recv_datas", objects); String jsonString = JSON.toJSONString(map); post.body(jsonString); HttpResponse response = post.execute(); String responseStr = response.body(); // 响应success JSONObject jsonObject = new JSONObject(); jsonObject.put("code", "SUCCESS"); jsonObject.put("message", "执行成功"); return jsonObject; } return null; } public org.apache.http.HttpResponse post(String url, String message, String authorization) throws Exception { SSLContext ctx = SSLContext.getInstance("TLS"); X509TrustManager tm = new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] xcs, String str) { } public void checkServerTrusted(X509Certificate[] xcs, String str) { } }; HttpClient http = new DefaultHttpClient(); ClientConnectionManager ccm = http.getConnectionManager(); ctx.init(null, new TrustManager[]{tm}, null); SSLSocketFactory ssf = new SSLSocketFactory(ctx); ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); SchemeRegistry registry = ccm.getSchemeRegistry(); registry.register(new Scheme("https", ssf, 443)); HttpPost post = new HttpPost(url); StringEntity myEntity = new StringEntity(message, ENCODING); post.setEntity(myEntity); post.setHeader("Authorization", SCHEMA + " " + authorization); post.setHeader("Accept", "application/json"); post.setHeader("Content-Type", "application/json"); return http.execute(post); } public final String getAuthorization(String body) throws IOException { String nonceStr = generateNonceStr(); long timestamp = generateTimestamp(); String message = LakalaConfig.getAppId() + "\n" + LakalaConfig.getMerchantNo() + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n"; System.out.println("getToken message : " + message); PrivateKey merchantPrivateKey = loadPrivateKey(new FileInputStream(new File(LakalaConfig.getPriKeyStr()))); String signature = this.sign(message.getBytes(ENCODING), merchantPrivateKey); String authorization = "appid=\"" + LakalaConfig.getAppId() + "\"," + "serial_no=\"" + LakalaConfig.getMerchantNo() + "\"," + "timestamp=\"" + timestamp + "\"," + "nonce_str=\"" + nonceStr + "\"," + "signature=\"" + signature + "\""; System.out.println("authorization message :" + authorization); return authorization; } public long generateTimestamp() { return System.currentTimeMillis() / 1000; } public String generateNonceStr() { char[] nonceChars = new char[32]; for (int index = 0; index < nonceChars.length; ++index) { nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length())); } return new String(nonceChars); } public static PrivateKey loadPrivateKey(InputStream inputStream) { try { ByteArrayOutputStream array = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) != -1) { array.write(buffer, 0, length); } String privateKey = array.toString("utf-8").replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", ""); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey))); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("当前Java环境不支持RSA", e); } catch (InvalidKeySpecException e) { throw new RuntimeException("无效的密钥格式"); } catch (IOException e) { throw new RuntimeException("无效的密钥"); } } public String sign(byte[] message, PrivateKey privateKey) { try { Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(privateKey); sign.update(message); return new String(Base64.encodeBase64(sign.sign())); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("当前Java环境不支持SHA256withRSA", e); } catch (SignatureException e) { throw new RuntimeException("签名计算失败", e); } catch (InvalidKeyException e) { throw new RuntimeException("无效的私钥", e); } } public final String getBody(HttpServletRequest request) { InputStreamReader in = null; try { in = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8); StringBuffer bf = new StringBuffer(); int len; char[] chs = new char[1024]; while ((len = in.read(chs)) != -1) { bf.append(new String(chs, 0, len)); } return bf.toString(); } catch (Exception e) { e.printStackTrace(); } finally { if (null != in) { try { in.close(); } catch (Exception e) { } } } return null; } public static X509Certificate loadCertificate(InputStream inputStream) { try { CertificateFactory cf = CertificateFactory.getInstance("X509"); X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream); cert.checkValidity(); return cert; } catch (CertificateExpiredException e) { throw new RuntimeException("证书已过期", e); } catch (CertificateNotYetValidException e) { throw new RuntimeException("证书尚未生效", e); } catch (CertificateException e) { throw new RuntimeException("无效的证书", e); } } private static boolean verify(X509Certificate certificate, byte[] message, String signature) { try { Signature sign = Signature.getInstance("SHA256withRSA"); sign.initVerify(certificate); sign.update(message); byte[] signatureB = Base64.decodeBase64(signature); return sign.verify(signatureB); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("当前Java环境不支持SHA256withRSA", e); } catch (SignatureException e) { throw new RuntimeException("签名验证过程发生了错误", e); } catch (InvalidKeyException e) { throw new RuntimeException("无效的证书", e); } } }