|
|
@@ -0,0 +1,446 @@
|
|
|
+package com.ruoyi.app.pay;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.ruoyi.app.order.dto.OrderPushBodyDto;
|
|
|
+import com.ruoyi.app.utils.PayPush;
|
|
|
+import com.ruoyi.app.utils.event.PushEventService;
|
|
|
+import com.ruoyi.app.utils.newebpay.NewebPay;
|
|
|
+import com.ruoyi.app.utils.newebpay.NewebPayConfig;
|
|
|
+import com.ruoyi.app.utils.newebpay.NewebPayEncryptUtil;
|
|
|
+import com.ruoyi.common.annotation.Anonymous;
|
|
|
+import com.ruoyi.common.annotation.RepeatSubmit;
|
|
|
+import com.ruoyi.common.core.controller.BaseController;
|
|
|
+import com.ruoyi.common.core.domain.AjaxResult;
|
|
|
+import com.ruoyi.common.utils.MessageUtils;
|
|
|
+import com.ruoyi.system.domain.InfoUser;
|
|
|
+import com.ruoyi.system.domain.IpnLog;
|
|
|
+import com.ruoyi.system.domain.PosOrder;
|
|
|
+import com.ruoyi.system.domain.PosOrderPayment;
|
|
|
+import com.ruoyi.system.domain.PosStoreNewebpay;
|
|
|
+import com.ruoyi.system.mapper.RiderPositionMapper;
|
|
|
+import com.ruoyi.system.service.IInfoUserService;
|
|
|
+import com.ruoyi.system.service.IIpnLogService;
|
|
|
+import com.ruoyi.system.service.IPosOrderPaymentService;
|
|
|
+import com.ruoyi.system.service.IPosOrderService;
|
|
|
+import com.ruoyi.system.service.IPosStoreNewebpayService;
|
|
|
+import com.ruoyi.system.utils.Auth;
|
|
|
+import com.ruoyi.system.utils.JwtUtil;
|
|
|
+import com.ruoyi.system.utils.OrderLogHelper;
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
+import jakarta.servlet.http.HttpServletResponse;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.web.bind.annotation.PostMapping;
|
|
|
+import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
+import org.springframework.web.bind.annotation.RequestParam;
|
|
|
+import org.springframework.web.bind.annotation.RestController;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.net.URLDecoder;
|
|
|
+import java.net.URLEncoder;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.Enumeration;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 蓝新金流(NewebPay) 线上支付 Controller。
|
|
|
+ *
|
|
|
+ * <p>本期实现:
|
|
|
+ * <ul>
|
|
|
+ * <li>{@code POST /pay/newebpay/create} — 发起 MPG 幕前支付(US1)。</li>
|
|
|
+ * <li>{@code POST /pay/newebpay/notify} — NotifyURL 支付结果回调(US2,@Anonymous)。</li>
|
|
|
+ * <li>{@code GET|POST /pay/newebpay/return} — ReturnURL 完成页引导(US2,@Anonymous)。</li>
|
|
|
+ * </ul>
|
|
|
+ * 单笔查询 /pay/newebpay/query(US5)后续补充。
|
|
|
+ *
|
|
|
+ * <p>回调成功后的业务链路(更新 payStatus + 订单日志 + 推送用户/商家/骑手)参照
|
|
|
+ * {@link PayController#payipn(HttpServletRequest)} 的 VNPay 实现。
|
|
|
+ *
|
|
|
+ * @author ruoyi
|
|
|
+ * @date 2026-06-22
|
|
|
+ */
|
|
|
+@RestController
|
|
|
+@RequestMapping("/pay/newebpay")
|
|
|
+public class NewebpayPayController extends BaseController {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(NewebpayPayController.class);
|
|
|
+
|
|
|
+ /** payType 取值:蓝新在线支付(发起时写入 pos_order.pay_type;具体方式 CREDIT/LINEPAY/APPLEPAY 由回调写入 pos_order_payment)。 */
|
|
|
+ public static final String PAY_TYPE_NEWEBPAY = "6";
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IPosOrderService posOrderService;
|
|
|
+ @Autowired
|
|
|
+ private IPosStoreNewebpayService posStoreNewebpayService;
|
|
|
+ @Autowired
|
|
|
+ private IPosOrderPaymentService posOrderPaymentService;
|
|
|
+ @Autowired
|
|
|
+ private NewebPay newebPay;
|
|
|
+ @Autowired
|
|
|
+ private IInfoUserService infoUserService;
|
|
|
+ @Autowired
|
|
|
+ private RiderPositionMapper riderPositionMapper;
|
|
|
+ @Autowired
|
|
|
+ private PushEventService pushEventService;
|
|
|
+ @Autowired
|
|
|
+ private OrderLogHelper orderLogHelper;
|
|
|
+ @Autowired
|
|
|
+ private IIpnLogService ipnLogService;
|
|
|
+ /** 复用 PayController 的可接单骑手异步推送(避免重写复杂调度)。 */
|
|
|
+ @Autowired
|
|
|
+ private PayController payController;
|
|
|
+
|
|
|
+ @Value("${newebpay.base-url}")
|
|
|
+ private String baseUrl;
|
|
|
+ @Value("${newebpay.notify-url}")
|
|
|
+ private String notifyUrl;
|
|
|
+ @Value("${newebpay.return-url}")
|
|
|
+ private String returnUrl;
|
|
|
+ @Value("${newebpay.front-result-url}")
|
|
|
+ private String frontResultUrl;
|
|
|
+ @Value("${newebpay.mpg-version}")
|
|
|
+ private String mpgVersion;
|
|
|
+
|
|
|
+ // ============================ US1:发起 MPG 幕前支付 ============================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发起蓝新 MPG 幕前支付。校验订单 → 查门店启用凭证 → 生成商店订单号 →
|
|
|
+ * 组装并加密 TradeInfo → 落流水 + 更新订单 payType → 返回 form 字段供前端 Form Post 跳转蓝新付款页。
|
|
|
+ */
|
|
|
+ @Anonymous
|
|
|
+ @Auth
|
|
|
+ @RepeatSubmit(interval = 1000, message = "请求过于频繁")
|
|
|
+ @PostMapping("/create")
|
|
|
+ public AjaxResult create(@RequestParam String orderid, HttpServletRequest request) {
|
|
|
+ String token = request.getHeader("token");
|
|
|
+ JwtUtil jwtUtil = new JwtUtil();
|
|
|
+ String userId;
|
|
|
+ try {
|
|
|
+ userId = jwtUtil.getusid(token);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return error(MessageUtils.message("no.order.id.error"));
|
|
|
+ }
|
|
|
+ if (userId == null || userId.isEmpty()) {
|
|
|
+ return error("请先登录");
|
|
|
+ }
|
|
|
+
|
|
|
+ PosOrder order = posOrderService.getOne(new QueryWrapper<PosOrder>().eq("dd_id", orderid));
|
|
|
+ if (order == null) {
|
|
|
+ return error(MessageUtils.message("no.order.id.error"));
|
|
|
+ }
|
|
|
+ if (order.getUserId() == null || !userId.equals(String.valueOf(order.getUserId()))) {
|
|
|
+ return error("无权操作该订单");
|
|
|
+ }
|
|
|
+ if (order.getPayStatus() != null && order.getPayStatus() == 1L) {
|
|
|
+ return error("订单已支付");
|
|
|
+ }
|
|
|
+ if (order.getAmount() == null || order.getAmount() <= 0) {
|
|
|
+ return error("订单金额异常");
|
|
|
+ }
|
|
|
+
|
|
|
+ Long storeId = order.getMdId();
|
|
|
+ PosStoreNewebpay cfg = posStoreNewebpayService.getEnabledConfig(storeId);
|
|
|
+ if (cfg == null) {
|
|
|
+ return error("该门店暂不支持线上支付");
|
|
|
+ }
|
|
|
+
|
|
|
+ String merchantOrderNo = genMerchantOrderNo(orderid);
|
|
|
+
|
|
|
+ // TradeInfo 明文(NDNF §4.2.1)
|
|
|
+ Map<String, Object> tradeParams = new LinkedHashMap<>();
|
|
|
+ tradeParams.put("MerchantID", cfg.getMerchantId());
|
|
|
+ tradeParams.put("RespondType", "JSON");
|
|
|
+ tradeParams.put("TimeStamp", String.valueOf(System.currentTimeMillis() / 1000L));
|
|
|
+ tradeParams.put("Version", mpgVersion);
|
|
|
+ tradeParams.put("MerchantOrderNo", merchantOrderNo);
|
|
|
+ tradeParams.put("Amt", order.getAmount());
|
|
|
+ tradeParams.put("ItemDesc", "order " + orderid);
|
|
|
+ tradeParams.put("NotifyURL", notifyUrl);
|
|
|
+ tradeParams.put("ReturnURL", returnUrl);
|
|
|
+ applyPaymentSwitch(tradeParams, cfg.getEnabledPayments());
|
|
|
+
|
|
|
+ NewebPayConfig npc = new NewebPayConfig(cfg.getMerchantId(), cfg.getHashKey(), cfg.getHashIv());
|
|
|
+ Map<String, String> form = newebPay.createMpgForm(baseUrl, npc, tradeParams, mpgVersion);
|
|
|
+
|
|
|
+ posOrderPaymentService.createPayment(orderid, merchantOrderNo, storeId, cfg.getMerchantId(), order.getAmount());
|
|
|
+
|
|
|
+ PosOrder upd = new PosOrder();
|
|
|
+ upd.setId(order.getId());
|
|
|
+ upd.setPayType(PAY_TYPE_NEWEBPAY);
|
|
|
+ upd.setPayUrl(form.get("gatewayUrl"));
|
|
|
+ posOrderService.saveOrUpdate(upd);
|
|
|
+
|
|
|
+ return success(MessageUtils.message("no.order.create.success"), form);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ============================ US2:NotifyURL 支付结果回调 ============================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 蓝新支付结果背景通知(@Anonymous,蓝新服务器 Form Post)。
|
|
|
+ *
|
|
|
+ * <p>记 IPN 日志 → 按 MerchantID 查凭证 → 验签 TradeSha → 解密 TradeInfo →
|
|
|
+ * 幂等(trade_no) + 金额校验 + 订单关联 → 成功则更新 payStatus=1 并推送用户/商家/骑手;
|
|
|
+ * 失败则记录。任何异常都不抛错响应(蓝新收到非成功会重试),但绝不错误更新订单。
|
|
|
+ */
|
|
|
+ @Anonymous
|
|
|
+ @PostMapping("/notify")
|
|
|
+ public JSONObject notify(HttpServletRequest request) {
|
|
|
+ JSONObject resp = new JSONObject();
|
|
|
+ resp.put("Status", "SUCCESS");
|
|
|
+ resp.put("Message", "OK");
|
|
|
+
|
|
|
+ Map<String, Object> form = collectForm(request);
|
|
|
+ String ip = new com.ruoyi.app.utils.IpUtils().getIpAddr(request);
|
|
|
+ String merchantId = (String) form.get("MerchantID");
|
|
|
+ String tradeInfo = (String) form.get("TradeInfo");
|
|
|
+ String tradeSha = (String) form.get("TradeSha");
|
|
|
+ String status = (String) form.get("Status");
|
|
|
+
|
|
|
+ // 记录 IPN 日志
|
|
|
+ try {
|
|
|
+ IpnLog ipnLog = new IpnLog();
|
|
|
+ ipnLog.setIp(ip);
|
|
|
+ ipnLog.setIpnLog(form.toString());
|
|
|
+ ipnLogService.insertIpnLog(ipnLog);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("记蓝新 IPN 日志失败", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ PosStoreNewebpay cfg = posStoreNewebpayService.getByMerchantId(merchantId);
|
|
|
+ if (cfg == null || cfg.getHashKey() == null || tradeInfo == null) {
|
|
|
+ log.warn("蓝新回调无匹配凭证或缺少 TradeInfo: merchantId={}", merchantId);
|
|
|
+ return resp;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验签
|
|
|
+ String expectedSha = NewebPayEncryptUtil.genTradeSha(tradeInfo, cfg.getHashKey(), cfg.getHashIv());
|
|
|
+ if (tradeSha == null || !tradeSha.equals(expectedSha)) {
|
|
|
+ log.warn("蓝新回调验签失败: merchantId={}, expected={}, got={}", merchantId, expectedSha, tradeSha);
|
|
|
+ return resp;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解密
|
|
|
+ Map<String, Object> d;
|
|
|
+ try {
|
|
|
+ d = parseQuery(NewebPayEncryptUtil.decrypt(tradeInfo, cfg.getHashKey(), cfg.getHashIv()));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("蓝新回调解密失败: merchantId={}", merchantId, e);
|
|
|
+ return resp;
|
|
|
+ }
|
|
|
+
|
|
|
+ String tradeNo = getStr(d, "TradeNo");
|
|
|
+ String merchantOrderNo = getStr(d, "MerchantOrderNo");
|
|
|
+
|
|
|
+ // 幂等:同 tradeNo 已成功则直接返回
|
|
|
+ PosOrderPayment exist = posOrderPaymentService.getByTradeNo(tradeNo);
|
|
|
+ if (exist != null && exist.getPayStatus() != null && exist.getPayStatus() == 1) {
|
|
|
+ return resp;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 订单关联
|
|
|
+ PosOrderPayment payment = posOrderPaymentService.getByMerchantOrderNo(merchantOrderNo);
|
|
|
+ if (payment == null) {
|
|
|
+ log.warn("蓝新回调无对应发起记录: merchantOrderNo={}", merchantOrderNo);
|
|
|
+ return resp;
|
|
|
+ }
|
|
|
+ String ddId = payment.getDdId();
|
|
|
+ PosOrder order = posOrderService.getOne(new QueryWrapper<PosOrder>().eq("dd_id", ddId));
|
|
|
+ if (order == null) {
|
|
|
+ log.warn("蓝新回调订单不存在: ddId={}", ddId);
|
|
|
+ return resp;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 金额校验
|
|
|
+ int amt = parseInt(getStr(d, "Amt"), -1);
|
|
|
+ if (amt < 0 || order.getAmount() == null || order.getAmount() != amt) {
|
|
|
+ log.error("蓝新回调金额不符: ddId={}, orderAmt={}, callbackAmt={}", ddId, order.getAmount(), amt);
|
|
|
+ return resp;
|
|
|
+ }
|
|
|
+
|
|
|
+ String payType = mapPaymentType(d);
|
|
|
+ String auth = getStr(d, "Auth");
|
|
|
+ Date payTime = parsePayTime(getStr(d, "PayTime"));
|
|
|
+
|
|
|
+ if ("SUCCESS".equals(status)) {
|
|
|
+ int n = posOrderPaymentService.markSuccess(ddId, merchantOrderNo, payment.getStoreId(),
|
|
|
+ merchantId, amt, tradeNo, payType, auth, payTime, d.toString());
|
|
|
+ if (n > 0) {
|
|
|
+ // 首次成功:更新订单支付状态并推送(state 保持 0 待商家接单)
|
|
|
+ handlePaymentSuccess(order, d);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ posOrderPaymentService.markFail(merchantOrderNo, tradeNo, payType, d.toString());
|
|
|
+ log.warn("蓝新回调交易失败: ddId={}, status={}, message={}", ddId, status, getStr(d, "Message"));
|
|
|
+ }
|
|
|
+ return resp;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 支付完成返回页(@Anonymous)。仅引导回前端结果页,<b>不</b>修改订单状态(状态以 notify 为准)。
|
|
|
+ */
|
|
|
+ @Anonymous
|
|
|
+ @RequestMapping(value = "/return", method = {org.springframework.web.bind.annotation.RequestMethod.GET,
|
|
|
+ org.springframework.web.bind.annotation.RequestMethod.POST})
|
|
|
+ public void returnCallback(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
|
|
+ String ddId = "";
|
|
|
+ try {
|
|
|
+ Map<String, Object> form = collectForm(request);
|
|
|
+ String merchantId = (String) form.get("MerchantID");
|
|
|
+ String tradeInfo = (String) form.get("TradeInfo");
|
|
|
+ PosStoreNewebpay cfg = posStoreNewebpayService.getByMerchantId(merchantId);
|
|
|
+ if (cfg != null && cfg.getHashKey() != null && tradeInfo != null) {
|
|
|
+ Map<String, Object> d = parseQuery(NewebPayEncryptUtil.decrypt(tradeInfo, cfg.getHashKey(), cfg.getHashIv()));
|
|
|
+ PosOrderPayment p = posOrderPaymentService.getByMerchantOrderNo(getStr(d, "MerchantOrderNo"));
|
|
|
+ if (p != null) {
|
|
|
+ ddId = p.getDdId();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("蓝新 ReturnURL 解析失败", e);
|
|
|
+ }
|
|
|
+ String sep = frontResultUrl.contains("?") ? "&" : "?";
|
|
|
+ response.sendRedirect(frontResultUrl + sep + "ddId=" + URLEncoder.encode(ddId, StandardCharsets.UTF_8));
|
|
|
+ }
|
|
|
+
|
|
|
+ // ============================ 支付成功业务链路(参照 PayController.payipn) ============================
|
|
|
+
|
|
|
+ /** 支付成功:更新订单 payStatus=1(state 不变)+ 订单日志 + 推送用户/商家/骑手。 */
|
|
|
+ private void handlePaymentSuccess(PosOrder order, Map<String, Object> decryptedMap) {
|
|
|
+ try {
|
|
|
+ PosOrder upd = new PosOrder();
|
|
|
+ upd.setId(order.getId());
|
|
|
+ upd.setState(0L);
|
|
|
+ upd.setPayStatus(1L);
|
|
|
+ posOrderService.saveOrUpdate(upd);
|
|
|
+
|
|
|
+ orderLogHelper.logSync(String.valueOf(order.getDdId()), 0, null, "系统", "系统收到蓝新支付成功回调");
|
|
|
+
|
|
|
+ InfoUser user = order.getUserId() == null ? null : infoUserService.getById(order.getUserId());
|
|
|
+ InfoUser shanghu = order.getShId() == null ? null : infoUserService.getById(order.getShId());
|
|
|
+ if (user == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ PayPush push = new PayPush();
|
|
|
+ Map<String, Object> orderMap = OrderPushBodyDto.getMap(String.valueOf(order.getDdId()), "1", 0, 1);
|
|
|
+ String json = OrderPushBodyDto.margeMapGetJsonString(orderMap, decryptedMap);
|
|
|
+
|
|
|
+ String title = MessageUtils.message("no.message.push.message");
|
|
|
+ push.apppush(user.getCid(), title, MessageUtils.message("no.message.push.payment.success"), json);
|
|
|
+ pushEventService.PublisherEvent(user.getUserId(), title, MessageUtils.message("no.message.push.payment.success"), json);
|
|
|
+
|
|
|
+ if (shanghu != null) {
|
|
|
+ push.shpush(shanghu.getCid(), title, MessageUtils.message("no.message.push.new.order"), json);
|
|
|
+ pushEventService.PublisherEvent(shanghu.getUserId(), title, MessageUtils.message("no.message.push.new.order"), json);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 可接单骑手推送(外卖),复用 PayController 的异步实现
|
|
|
+ try {
|
|
|
+ String body = OrderPushBodyDto.getJson(String.valueOf(order.getDdId()), String.valueOf(order.getState()), 1);
|
|
|
+ payController.sendAcceptRiderPush(order, push, riderPositionMapper, body);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("蓝新支付成功骑手推送异常: ddId={}", order.getDdId(), e);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("蓝新支付成功业务处理异常: ddId={}", order.getDdId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ============================ 辅助 ============================
|
|
|
+
|
|
|
+ private static String genMerchantOrderNo(String orderid) {
|
|
|
+ String safe = orderid == null ? "" : orderid.replaceAll("[^A-Za-z0-9_]", "");
|
|
|
+ return "NB" + safe;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void applyPaymentSwitch(Map<String, Object> params, String enabledPayments) {
|
|
|
+ if (enabledPayments == null || enabledPayments.isEmpty()) {
|
|
|
+ params.put("CREDIT", 1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String eps = enabledPayments.toUpperCase();
|
|
|
+ params.put("CREDIT", eps.contains("CREDIT") ? 1 : 0);
|
|
|
+ params.put("LINEPAY", eps.contains("LINEPAY") ? 1 : 0);
|
|
|
+ params.put("APPLEPAY", eps.contains("APPLEPAY") ? 1 : 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 收集 form 参数到 Map(值非空的单值参数)。 */
|
|
|
+ private static Map<String, Object> collectForm(HttpServletRequest request) {
|
|
|
+ Map<String, Object> m = new LinkedHashMap<>();
|
|
|
+ Enumeration<String> names = request.getParameterNames();
|
|
|
+ while (names.hasMoreElements()) {
|
|
|
+ String n = names.nextElement();
|
|
|
+ String[] vals = request.getParameterValues(n);
|
|
|
+ if (vals != null && vals.length == 1 && !vals[0].isEmpty()) {
|
|
|
+ m.put(n, vals[0]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return m;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 解析 a=1&b=2(URL decode)为 Map。 */
|
|
|
+ private static Map<String, Object> parseQuery(String query) {
|
|
|
+ Map<String, Object> m = new LinkedHashMap<>();
|
|
|
+ if (query == null || query.isEmpty()) {
|
|
|
+ return m;
|
|
|
+ }
|
|
|
+ for (String pair : query.split("&")) {
|
|
|
+ int idx = pair.indexOf('=');
|
|
|
+ if (idx <= 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String k = urlDecode(pair.substring(0, idx));
|
|
|
+ String v = idx + 1 <= pair.length() ? urlDecode(pair.substring(idx + 1)) : "";
|
|
|
+ m.put(k, v);
|
|
|
+ }
|
|
|
+ return m;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String urlDecode(String s) {
|
|
|
+ try {
|
|
|
+ return URLDecoder.decode(s, StandardCharsets.UTF_8);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return s;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String getStr(Map<String, Object> m, String k) {
|
|
|
+ Object v = m.get(k);
|
|
|
+ return v == null ? "" : v.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int parseInt(String s, int def) {
|
|
|
+ try {
|
|
|
+ return Integer.parseInt(s);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return def;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Date parsePayTime(String s) {
|
|
|
+ if (s == null || s.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(s);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 蓝新回调支付方式映射:优先 PaymentMethod(APPLEPAY),否则 PaymentType(CREDIT/LINEPAY...)。 */
|
|
|
+ private static String mapPaymentType(Map<String, Object> d) {
|
|
|
+ String pm = getStr(d, "PaymentMethod");
|
|
|
+ if (!pm.isEmpty()) {
|
|
|
+ return pm.toUpperCase();
|
|
|
+ }
|
|
|
+ return getStr(d, "PaymentType").toUpperCase();
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO US5: POST /pay/newebpay/query(单笔查询 + 补单)
|
|
|
+}
|