diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java b/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java index 078fa10b..baae74b8 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java @@ -1,9 +1,14 @@ package com.suisung.mall.common.utils; import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import java.io.*; import java.net.URLEncoder; import java.security.MessageDigest; @@ -29,6 +34,7 @@ public final class StringUtils extends org.apache.commons.lang3.StringUtils { private static final String ALL_TEMP = INT_TEMP + STR_TEMP; private static final Random RANDOM = new Random(); private static final Logger logger = LoggerFactory.getLogger(StringUtils.class); + private static final ObjectMapper objectMapper = new ObjectMapper(); public static void main(String[] args) { System.out.println(removeProvinceCityDistrict("广西壮族自治区贵港市桂平市西山镇新安街粤桂花城1102号")); @@ -223,6 +229,7 @@ public final class StringUtils extends org.apache.commons.lang3.StringUtils { /** * 生成唯一码 + * * @param length * @return */ @@ -271,6 +278,51 @@ public final class StringUtils extends org.apache.commons.lang3.StringUtils { return fullAddress; } + /** + * 判断字符串是否是XML或JSON格式 + * + * @param input + * @return XML或JSON或None + */ + public static String isXMLOrJSON(String input) { + if (input == null || input.trim().isEmpty()) { + return "None"; + } + input = input.trim(); + + // 快速初步判断 + if (input.startsWith("{") || input.startsWith("[")) { + if (isValidJson(input)) { + return "JSON"; + } + } else if (input.startsWith("<")) { + if (isValidXml(input)) { + return "XML"; + } + } + return "None"; + } + + private static boolean isValidJson(String json) { + try { + objectMapper.readTree(json); + return true; + } catch (Exception e) { + return false; + } + } + + private static boolean isValidXml(String xml) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new InputSource(new StringReader(xml))); + return true; + } catch (Exception e) { + return false; + } + } + /** * 生成的随机数类型 diff --git a/mall-gateway/src/main/java/com/suisung/mall/filter/AuthGlobalFilter.java b/mall-gateway/src/main/java/com/suisung/mall/filter/AuthGlobalFilter.java index 3c92f120..a989bd5f 100644 --- a/mall-gateway/src/main/java/com/suisung/mall/filter/AuthGlobalFilter.java +++ b/mall-gateway/src/main/java/com/suisung/mall/filter/AuthGlobalFilter.java @@ -42,6 +42,7 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered { return chain.filter(exchange); } } + try { //从token中解析用户信息并设置到Header中去 String realToken = token.replace(AuthConstant.JWT_TOKEN_PREFIX, ""); diff --git a/mall-gateway/src/main/resources/application.yml b/mall-gateway/src/main/resources/application.yml index 321a87a4..85ece989 100644 --- a/mall-gateway/src/main/resources/application.yml +++ b/mall-gateway/src/main/resources/application.yml @@ -70,6 +70,7 @@ secure: - "/admin/account/account-user-base/login" - "/static/image/**" - "/mobile/pay/index/notify_url" + #- "/mobile/pay/index/lkl_wxPay_notify_url" #拉卡拉微信支付回调 - "/mobile/pay/index/return_url" - "/shop/static/**" - "/mobile/shop/qrcode/getQrcode" diff --git a/mall-pay/pom.xml b/mall-pay/pom.xml index 9923af8f..c491acd1 100644 --- a/mall-pay/pom.xml +++ b/mall-pay/pom.xml @@ -79,6 +79,13 @@ + + + com.github.wechatpay-apiv3 + wechatpay-java + 0.2.15 + + org.springframework.boot diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/controller/mobile/IndexController.java b/mall-pay/src/main/java/com/suisung/mall/pay/controller/mobile/IndexController.java index 790ce7b8..d4f2b3e6 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/controller/mobile/IndexController.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/controller/mobile/IndexController.java @@ -243,6 +243,12 @@ public class IndexController extends BaseControllerImpl { return payUserPayService.notifyUrl(request, response, "wx_native"); } + @ApiOperation(value = "拉卡拉在微信回调通知", notes = "拉卡拉在微信回调通知") + @RequestMapping(value = "/lkl_wxPay_notify_url", method = RequestMethod.POST) + public String lklWxNotifyUrl(HttpServletRequest request) { + return payUserPayService.lklNotifyUrl(request); + } + @RequestMapping(value = "/pay_state_detection", method = RequestMethod.GET) public CommonResult payStateDetection(@RequestParam String order_id) { UserDto userDto = getCurrentUser(); diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaService.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaService.java index d6df9e5f..e25be858 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaService.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaService.java @@ -10,6 +10,7 @@ package com.suisung.mall.pay.service; import cn.hutool.json.JSONObject; import com.suisung.mall.common.pojo.res.PayAccRespFieldsRes; +import com.wechat.pay.java.service.payments.nativepay.NativePayService; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -21,6 +22,13 @@ public interface LakalaService { */ void doInit(); + /** + * 初始化微信V3支付参数 + * + * @return + */ + NativePayService initWxV3SDK(); + JSONObject transPreOrder(HttpServletRequest request, HttpServletResponse response, String orderId); /** @@ -38,5 +46,5 @@ public interface LakalaService { * @param remark 备注 * @return */ - PayAccRespFieldsRes transPreOrder(String merchantNo, String termNo, String xcxAppId, String openId, String orderId, String subject, String totalAmount, String notifyURL, String requestIP, String remark); + JSONObject transPreOrder(String merchantNo, String termNo, String xcxAppId, String openId, String orderId, String subject, String totalAmount, String notifyURL, String requestIP, String remark); } diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/PayUserPayService.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/PayUserPayService.java index 1a0bef3e..80497017 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/PayUserPayService.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/PayUserPayService.java @@ -31,6 +31,13 @@ public interface PayUserPayService extends IBaseService { String notifyUrl(HttpServletRequest request, HttpServletResponse response, String payment_channel_code); + /** + * lakala 支付回调 + * @param request + * @return + */ + String lklNotifyUrl(HttpServletRequest request); + void returnUrl(HttpServletRequest request, HttpServletResponse response, String payment_channel_code); /** diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaServiceImpl.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaServiceImpl.java index 13c844d0..d8e4ebd9 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaServiceImpl.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaServiceImpl.java @@ -10,6 +10,7 @@ package com.suisung.mall.pay.service.impl; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.ijpay.core.kit.IpKit; import com.lkl.laop.sdk.Config2; @@ -22,24 +23,42 @@ import com.suisung.mall.common.exception.ApiException; import com.suisung.mall.common.pojo.res.PayAccRespFieldsRes; import com.suisung.mall.common.utils.I18nUtil; import com.suisung.mall.pay.service.LakalaService; +import com.wechat.pay.java.core.exception.ServiceException; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; + +import com.wechat.pay.java.core.Config; +import com.wechat.pay.java.core.RSAAutoCertificateConfig; +import com.wechat.pay.java.service.payments.nativepay.NativePayService; +import com.wechat.pay.java.service.payments.nativepay.model.Amount; +import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest; +import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.*; + @Slf4j @Service public class LakalaServiceImpl implements LakalaService { - private static volatile boolean init = false; + private static volatile NativePayService wxV3Service= null; + /** * 服务地址 */ @@ -121,6 +140,30 @@ public class LakalaServiceImpl implements LakalaService { return LKLSDK.init(config); } + @Override + public NativePayService initWxV3SDK() { + if (wxV3Service!=null) { + return wxV3Service; + } + + Config config = + new RSAAutoCertificateConfig.Builder() + .merchantId("1684833481") + .privateKey(getResourceFile("payKey/wx/apiclient_key.pem")) + .privateKeyFromPath("privateKeyPath") + .merchantSerialNumber("30314B93B394352C6A4616B7611C23402041ADE2") + .apiV3Key("n8tgyMKKAov9lVJuczMOLUjXr5IeZjce") + .build(); + // 构建service + NativePayService service = new NativePayService.Builder().config(config).build(); + if(service==null){ + throw new ApiException(I18nUtil._("微信SDKV3初始化失败!")); + } + + wxV3Service = service; + return service; + } + @Override public cn.hutool.json.JSONObject transPreOrder(HttpServletRequest request, HttpServletResponse response, String orderId) { // 1. 配置初始化 @@ -157,7 +200,7 @@ public class LakalaServiceImpl implements LakalaService { //4. 响应 return JSONUtil.parseObj(responseStr); } catch (SDKException e) { - log.error("transPreOrder error", e); + LakalaServiceImpl.log.error("transPreOrder error", e); throw new ApiException(I18nUtil._("获取公众号绑定信息失败!"), e); } @@ -177,9 +220,9 @@ public class LakalaServiceImpl implements LakalaService { * @param requestIP 请求ip * @param remark 备注 * @return - * */ + */ @Override - public PayAccRespFieldsRes transPreOrder(String merchantNo, String termNo, String xcxAppId, String openId, String orderId, String subject, String totalAmount, String notifyURL, String requestIP, String remark) { + public JSONObject transPreOrder(String merchantNo, String termNo, String xcxAppId, String openId, String orderId, String subject, String totalAmount, String notifyURL, String requestIP, String remark) { // 1. 配置初始化 doInit(); @@ -218,20 +261,24 @@ public class LakalaServiceImpl implements LakalaService { v3LabsTransPreorderWechatReq.setAccBusiFields(wechatBus); try { + log.info("拉卡拉预下单请求参数:{}", JSONUtil.toJsonStr(v3LabsTransPreorderWechatReq)); + //3. 发送请求 String responseStr = LKLSDK.httpPost(v3LabsTransPreorderWechatReq); + log.info("拉卡拉预下单响应数据:{}", responseStr); if (StrUtil.isBlank(responseStr)) { return null; } - PayAccRespFieldsRes res = JSONUtil.parseObj(responseStr).get("acc_resp_fields", PayAccRespFieldsRes.class); + JSONObject res = JSONUtil.parseObj(responseStr); + //4. 响应 return res; } catch (SDKException e) { log.error("拉卡拉支付出错:", e); - throw new ApiException(I18nUtil._("支付失败:"), e); + throw new ApiException(I18nUtil._("支付失败!"), e); } - } + } diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeDepositServiceImpl.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeDepositServiceImpl.java index 5025f5c2..bf7aea60 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeDepositServiceImpl.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeDepositServiceImpl.java @@ -203,6 +203,7 @@ public class PayConsumeDepositServiceImpl extends BaseServiceImpl params = UnifiedOrderModel.builder() - .appid(field.getApp_id()) - .mch_id(field.getSub_mch_id()) - .openid(openId) - .out_trade_no(out_trade_no) - .total_fee(total_fee) // 订单总金额,单位为分 - .notify_url(notifyUrl) - .trade_type(TradeType.JSAPI.getTradeType()) - .spbill_create_ip(requestIP) - .time_start(DateUtil.format(new Date(), "yyyyMMddHHmmss")) - .nonce_str(field.getNonce_str()) - .body(subject) // 商品描述 - .goods_tag(trade_remark)// 订单优惠标记 - .attach("") //自定义数据说明 - .build() - .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256); - - String xmlResult = WxPayApi.pushOrder(false, params); - Map resultMap = WxPayKit.xmlToMap(xmlResult); - String returnCode = resultMap.get("return_code"); - String returnMsg = resultMap.get("return_msg"); - +// logger.info("拉卡拉预支付返回参数:{}", JSONUtil.toJsonStr(field)); Map data = new HashMap(); data.put("code", 1); data.put("data", new Object()); - data.put("message", returnMsg); - if (!WxPayKit.codeIsOk(returnCode)) { + if (lakalaRespJSON == null || !lakalaRespJSON.getStr("code").equals("BBS00000")) { + data.put("message", lakalaRespJSON.getStr("msg")); setResponseBody(response, data); return; } - String resultCode = resultMap.get("result_code"); - if (!WxPayKit.codeIsOk(resultCode)) { - setResponseBody(response, data); - return; - } - - // 预支付订单 Id - String prepayId = resultMap.get("prepay_id"); - Map packageParams = Convert.toMap(String.class, Object.class, WxPayKit.prepayIdCreateSign(prepayId, appId, wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256)); - data.put("status", 200); + // 统一下单请求参数构建 + cn.hutool.json.JSONObject field = (cn.hutool.json.JSONObject) lakalaRespJSON.getByPath("resp_data.acc_resp_fields"); + Map params = new HashMap(); + params.put("package", "prepay_id=" + field.getStr("prepay_id")); + params.put("nonceStr", field.getStr("nonce_str")); + params.put("timeStamp", field.getStr("time_stamp")); + params.put("paySign", field.getStr("pay_sign")); + params.put("signType", field.getStr("sign_type")); + params.put("appId", field.getStr("app_id")); Map result = new HashMap(); - result.put("data", packageParams); + result.put("data", params); result.put("statusCode", 200); + + data.put("status", 200); data.put("data", result); + data.put("message", "OK"); + + logger.info("拉卡拉调起支付的参数:{}", JSONUtil.toJsonStr(data)); + setResponseBody(response, data); } @@ -1145,8 +1133,10 @@ public class PayUserPayServiceImpl extends BaseServiceImpl params = new HashMap<>(); if (ObjectUtil.equal("wx_native", payment_channel_code)) { - String xmlMsg = HttpKit.readData(request); - params = WxPayKit.xmlToMap(xmlMsg); + + String dataStr = HttpKit.readData(request); + + params = WxPayKit.xmlToMap(dataStr); trade_type = params.get("trade_type"); order_id = params.get("out_trade_no"); } @@ -1200,7 +1190,7 @@ public class PayUserPayServiceImpl extends BaseServiceImpl params; + try { + lakalaService.doInit(); + + // spring-boot 2.x 直接调用读取请求体并验签方法,返回请求体 + String body = LKLSDK.notificationHandle(request); + if (StrUtil.isBlank(body)) { + return lklNotifyMsg(false, "验签失败!"); + } + // logger.debug("支付回调 body 数据:{}", body); + + // 拉卡拉返回 json 格式的 数据 + params = Convert.toMap(String.class, String.class, JSONUtil.parseObj(body)); + String order_id = getParameter("out_trade_no"); + if(StrUtil.isBlank(order_id)){ + order_id = params.getOrDefault("out_trade_no",""); + } + logger.debug("支付回调 params 数据:{}", params); + + String authorization = request.getHeader("Authorization"); +// logger.debug("支付回调 Authorization 数据:{}", authorization); + Map authMap = LakalaUtil.getLakalaAuthorizationMap(authorization); + if (authMap != null && authMap.get("signature") != null) { + params.put("sign", authMap.get("signature")); + } + + // 基于安全考虑,检测支付模式及数据 + // 判断是门店 店铺 平台 + Integer payment_store_id = 0; + Integer payment_chain_id = 0; + if (!accountBaseConfigService.getTradeModePlantform()) { + // 不可以联合支付。 + QueryWrapper tradeQueryWrapper = new QueryWrapper<>(); + tradeQueryWrapper.eq("order_id", order_id); + PayConsumeTrade trade_row_tmp = payConsumeTradeService.findOne(tradeQueryWrapper); + + // 固定死, 是门店收银还是店铺收银。, 统一店铺收银 + payment_store_id = trade_row_tmp.getStore_id(); + } + + QueryWrapper channelQueryWrapper = new QueryWrapper<>(); + channelQueryWrapper.eq("payment_channel_code", "lakala"); + PayPaymentChannel payPaymentChannel = payPaymentChannelService.findOne(channelQueryWrapper); + Integer payment_channel_id = payPaymentChannel.getPayment_channel_id(); + + // 插入充值记录 + + PayConsumeDeposit notify_row = createNotify(params, payPaymentChannel); + notify_row.setOrder_id(order_id); + notify_row.setStore_id(payment_store_id); // 所属店铺 + notify_row.setChain_id(payment_chain_id); // 所属门店 + notify_row.setPayment_channel_id(payment_channel_id); + + BigDecimal zero = BigDecimal.ZERO; + + // 判断是否联合支付 + PayConsumeTradeCombine tradeCombine = payConsumeTradeCombineService.get(order_id); + TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition); + try { + if (tradeCombine != null && StrUtil.isNotBlank(tradeCombine.getOrder_ids())) { + notify_row.setOrder_id(tradeCombine.getOrder_ids()); + } + + if (!payConsumeDepositService.processDeposit(notify_row, zero, zero, zero, zero, zero)) { + log.error("支付失败!"); + return lklNotifyMsg(false, "支付失败!"); + } + + transactionManager.commit(transactionStatus); + } catch (Exception e) { + transactionManager.rollback(transactionStatus); + log.error("支付失败,错误信息:", e); + throw new ApiException(e.getMessage()); + } + + return lklNotifyMsg(true, ""); + + } catch (Exception e) { + logger.error("验签发生异常:" + e.getMessage() + ", {}", e); + return lklNotifyMsg(false, "验签发生异常!"); + } + } + /** * 获取支付异步请求返回值(多种支付方式) * @@ -1288,6 +1364,13 @@ public class PayUserPayServiceImpl extends BaseServiceImpl retData = new HashMap<>(); + retData.put("code", code ? "SUCCESS" : "FAIL"); + retData.put("message", message); + return JSONUtil.toJsonStr(retData); + } + /** * 创建支付回调参数对象 @@ -1319,6 +1402,9 @@ public class PayUserPayServiceImpl extends BaseServiceImpl params, Integer payment_channel_id, PayConsumeDeposit notify_row) { + BigDecimal deposit_total_fee = NumberUtil.div(Convert.toBigDecimal(params.get("total_amount")), 100); + String deposit_sign = ObjectUtil.defaultIfNull(params.get("sign"), ""); + Date deposit_gmt_payment = Convert.toDate(params.get("trade_time"), new Date()); + + notify_row.setDeposit_trade_no(params.get("trade_no")); + notify_row.setOrder_id(params.get("out_trade_no")); + notify_row.setDeposit_quantity(1); + notify_row.setDeposit_seller_id(params.get("sub_mch_id")); + notify_row.setDeposit_is_total_fee_adjust(0); + notify_row.setDeposit_total_fee(deposit_total_fee); + notify_row.setDeposit_price(BigDecimal.ZERO); + notify_row.setDeposit_buyer_id(params.get("user_id2"));// openid + notify_row.setDeposit_gmt_payment(deposit_gmt_payment); + notify_row.setDeposit_service("JSAPI"); + notify_row.setDeposit_sign(deposit_sign); + notify_row.setDeposit_sign_type("SHA256withRSA"); + notify_row.setDeposit_extra_param(JSONUtil.toJsonStr(notify_row)); + notify_row.setPayment_channel_id(payment_channel_id); + + notify_row.setDeposit_payment_type(StateCode.PAYMENT_TYPE_ONLINE); + notify_row.setDeposit_notify_time(new Date()); } /** diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/utils/LakalaUtil.java b/mall-pay/src/main/java/com/suisung/mall/pay/utils/LakalaUtil.java new file mode 100644 index 00000000..1af21c53 --- /dev/null +++ b/mall-pay/src/main/java/com/suisung/mall/pay/utils/LakalaUtil.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.pay.utils; + +import cn.hutool.core.util.StrUtil; +import com.suisung.mall.common.exception.ApiException; +import com.suisung.mall.common.utils.I18nUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.io.ClassPathResource; + +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.*; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class LakalaUtil { + + /** + * 读取证书文件内容 + * + * @param fileName recource 文件夹下的路径,如:palyKey/wx/lakala_public_key.cer + * @return + */ + public static String getResourceFile(String fileName) { + StringBuilder stringBuilder = new StringBuilder(); + try (InputStream inputStream = new ClassPathResource(fileName).getInputStream(); + InputStreamReader inputStreamReader = new InputStreamReader(inputStream); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { + String line; + while ((line = bufferedReader.readLine()) != null) { + stringBuilder.append(line).append("\n"); + } + String content = stringBuilder.toString(); + // log.info("证书内容:{}", content); + return content; + } catch (IOException e) { + // 记录异常信息 + log.error(e.getMessage()); + return ""; + } + } + + /** + * 获取 body 请求数据 + * + * @param request + * @return + */ + public static 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) { + log.error("请求头部取数据异常:{}", e); + throw new ApiException(I18nUtil._("获取请求数据失败!"), e); + } finally { + if (null != in) { + try { + in.close(); + } catch (Exception e) { + log.error("流关闭异常:{}", e); + } + } + } + } + + /** + * 获取证书信息 + * @param inputStream + * @return + */ + public static X509Certificate loadCertificate(InputStream inputStream) { + try { + CertificateFactory cf = CertificateFactory.getInstance("X509"); + X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream); + String publicKeyBase64 = Base64.encodeBase64String(cert.getPublicKey().getEncoded()); + System.out.println(publicKeyBase64); + 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); + } + } + + + /** + * 签名验证 + * @param certificate + * @param message + * @param signature + * @return + */ + public 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); + } + } + + /** + * 解析请求头中的认证签名等信息 + * 参考:https://o.lakala.com/#/home/document/detail?id=36 + * @param authorization 格式如下 + * LKLAPI-SHA256withRSA timestamp="1643271327", + * nonce_str="rQCbASKattHx", + * signature=" + * iPSycbakMt7AgjmwbtaweDVI/RLsQnGvOGiVM93haFkPpT/BxUprYx/GKFLQZebSQMvBfbeWinmnOJlqd3bXgye41BUAVmbItSTOzaQhNyS2kbDwXXGJWmT84aeJWHUB05BWB8ng + * /+X7jrPtsenC6aO7Xgh8jNylJlkU59TKCi7BPGbyHo6pAWJl/Bus0IQps1ay+Eo6Ks3Ins3COV7 + * /lmu5p5FD7TAZsfP+ZvMFObLJOrDQeBTMFKFFWj4ZkjNzNlQqZWlfLv4yLns + * /dKTDLDy5tRO5zwunW+li5YLcwOVf3tbevNFtg53WoBhQnwf838WNvY9zfRhOpCc4fBlWAA==" + * + * @return + */ + public static Map getLakalaAuthorizationMap(String authorization) { + Map map = new HashMap(); + authorization = authorization.trim(); + int bpos = authorization.indexOf(" "); + String authType = authorization.substring(0, bpos); + String[] typeArr = authType.split("-"); + if (typeArr.length > 1) { + map.put("signSystemCode", typeArr[0]); + map.put("signAlgorithm", typeArr[1]); + } + + String signInfo = authorization.substring(bpos + 1); + String[] infoArr = signInfo.split(","); + String[] var7 = infoArr; + int var8 = infoArr.length; + + for(int var9 = 0; var9 < var8; ++var9) { + String info = var7[var9]; + if (info.contains("=")) { + int fpos = info.indexOf("="); + String value = info.substring(fpos + 1).trim(); + value = value.substring(1, value.length() - 1); + map.put(info.substring(0, fpos).trim(), value); + } + } + + return map; + } +} diff --git a/mall-pay/src/main/resources/bootstrap-prod.yml b/mall-pay/src/main/resources/bootstrap-prod.yml index 258c1593..1cc01fd4 100644 --- a/mall-pay/src/main/resources/bootstrap-prod.yml +++ b/mall-pay/src/main/resources/bootstrap-prod.yml @@ -117,8 +117,6 @@ lakala: server_url: https://s2.lakala.com #应用Id app_id: OP10000439 - #商户号 - merchant_no: 8226330599900LN #商户证书序列号 serial_no: 1737359895636 #商户证书 @@ -127,5 +125,7 @@ lakala: api_pri_key_path: payKey/lakala/prod/api_private_key.pem #拉卡拉平台证书 lkl_platform_cer_path: payKey/lakala/prod/lkl_platform.cer + #商户号 + merchant_no: 8226330599900LN #终端号码,M0780629(B2B收银台) M0780798(专业化扫码) term_no: M0780798 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java index 79bd93f9..b2fcec4f 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java @@ -131,45 +131,9 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl 0) { - // 打印机已经存在的情况 -// if (existRecord.getFlag() == CommonConstant.Disable2) { -// // 打印机没有绑定飞鹅平台 -// UpdateWrapper updateWrapper = new UpdateWrapper(); -// updateWrapper.eq("printer_id", existRecord.getPrinter_id()); -// updateWrapper.set("updated_by", userId); -// updateWrapper.set("updated_at", new Date()); -// -// // 往厂商添加打印机 -// // "922441475#r6ZXPvHH#核销柜台"; -// boolean success = feieUtil.addPrinter(String.format("%s#%s#%s", existRecord.getPrinter_sn(), existRecord.getPrinter_key(), existRecord.getPrinter_name())); -// if (success) { -// updateWrapper.set("flag", CommonConstant.Enable); -// } -// -// update(updateWrapper); -// String msg = "添加成功"; -// if (!success) { -// msg = msg + ",但未绑定成功,请检查打印机编号和密钥是否正确。"; -// } -// -// return CommonResult.success(null, msg); -// } - return CommonResult.success(null, "打票机已添加,请勿重复操作"); } -// String msg = "添加成功"; - // 往厂商添加打印机 - // "922441475#r6ZXPvHH#核销柜台"; -// boolean success = feieUtil.addPrinter(String.format("%s#%s#%s", record.getPrinter_sn(), record.getPrinter_key(), record.getPrinter_name())); -// if (success) { -// -// if (!success) { -// msg = msg + ",但打印机绑定未成功,请检查打印机编号和密钥是否正确。"; -// } -// record.setFlag(CommonConstant.Enable); -// } - if (add(record)) { return CommonResult.success(null, "添加成功"); } @@ -220,19 +184,6 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl queryWrapper2 = new QueryWrapper<>(); @@ -364,14 +315,14 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl binding = shopOrderBaseService.getOrderPrintInfo(orderId, StateCode.ORDER_PAID_STATE_YES); if (binding == null) { - logger.info("订单{}信息无法获取,无法打印小票。", orderId); + logger.error("订单{}信息无法获取,无法打印小票。", orderId); return false; } @@ -380,7 +331,7 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl printerList = selectPrinterList(storeId); if (CollUtil.isEmpty(printerList)) { // 店铺没有打印机,不再往下执行打印工作 - logger.info("店铺{}未添加打票机,无法打印小票。", storeId); + logger.error("店铺{}未添加打票机,无法打印小票。", storeId); return false; } @@ -388,14 +339,14 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl> respList = feieUtil.printContentByList(printerSnList, printContent); if (respList == null || CollUtil.isEmpty(respList)) { // 只需一台打印机能打印,就算成功了 - logger.info("订单{}信息打印,调用飞鹅打印机打印失败。", orderId); + logger.error("订单{}信息打印,调用飞鹅打印机打印失败。", orderId); return false; } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java index f18bfb15..cf0565cc 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java @@ -179,8 +179,6 @@ public class SyncThirdDataServiceImpl implements SyncThirdDataService { } } - // 处理商品类型 - } ShopBaseProductCategory productCategoryTemp = productCategoryService.getCategoryByName(list.get(i).getParent_id(), list.get(i).getCategory_name(), list.get(i).getStore_id());