临时提交
This commit is contained in:
parent
b78c4e7858
commit
c749ad40de
|
@ -0,0 +1,97 @@
|
|||
package com.ruoyi.common.pay.tm;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ruoyi.common.utils.SnowFlakeUtil;
|
||||
import com.ruoyi.common.utils.http.HttpUtils;
|
||||
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Application {
|
||||
|
||||
private final static String HTTP = "https://pos.weixincore.com";
|
||||
private final static String SIGNKEY = "ac6d97e67b444b7a43edfc9182634786";
|
||||
|
||||
public static void main(String[] args) {
|
||||
orderQuery("tmwx1727172604248");
|
||||
// miniPay();
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单查询
|
||||
*/
|
||||
public static void orderQuery(String outTradeId) {
|
||||
HashMap<String, Object> body = new HashMap<>();
|
||||
body.put("outTradeId", outTradeId);
|
||||
body.put("terminalType", "1");
|
||||
body.put("shopId", "488");
|
||||
doPost("/open/Pay/orderQuery", body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款
|
||||
*/
|
||||
public static void refund() {
|
||||
HashMap<String,Object> body = new HashMap<>();
|
||||
body.put("refundFee", "0.01");
|
||||
body.put("terminalType", "1");
|
||||
body.put("tradeId", "1");
|
||||
body.put("shopId", "488");
|
||||
doPost("/open/Pay/refund", body);
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * jsapi支付
|
||||
// */
|
||||
// public static void pay() {
|
||||
// HashMap<String, String> body = new HashMap<String, String>();
|
||||
// body.put("payAmount", "1");
|
||||
// body.put("terminalType", "1");
|
||||
// body.put("shopId", "488");
|
||||
// // 填充必填字段
|
||||
// body.put("payType", "wx_pay"); // 支付方式,可以是 wx.pay, ali.pay, union.online
|
||||
// body.put("outTradeId", "tradeId123"); // 商户订单号
|
||||
// body.put("body", "商品描述"); // 商品描述
|
||||
// body.put("notifyUrl", "https://yourdomain.com/notify"); // 异步回调URL
|
||||
// body.put("frontUrl", "https://yourdomain.com/front"); // 前端页面跳转URL
|
||||
// doPost("/open/Pay/unifiedOrder", body);
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* 小程序支付
|
||||
*/
|
||||
public static void miniPay() {
|
||||
HashMap<String, Object> body = new HashMap<>();
|
||||
body.put("payAmount", "1");
|
||||
body.put("terminalType", "1");
|
||||
body.put("shopId", "488");
|
||||
// 填充必填字段
|
||||
body.put("payType", "wx_pay"); // 支付方式,可以是 wx.pay, ali.pay, union.online
|
||||
body.put("outTradeId", SnowFlakeUtil.newId()); // 商户订单号
|
||||
System.out.println("----------------------------------:"+body.get("outTradeId"));
|
||||
body.put("openid", "o6yEK7Z0OdWM2N_d8ehItn-5NBH8"); // openid
|
||||
body.put("body", "商品描述"); // 商品描述
|
||||
body.put("notifyUrl", "https://yourdomain.com/notify"); // 异步回调URL
|
||||
body.put("frontUrl", "https://yourdomain.com/front"); // 前端页面跳转URL
|
||||
doPost("/open/Pay/miniPay", body);
|
||||
}
|
||||
|
||||
private static void doPost(String url, HashMap<String, Object> body) {
|
||||
body.put("developerId", "100001");
|
||||
body.put("version", "1.0");
|
||||
body.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
|
||||
body.put("nonceStr", TmPayUtil.getRandomString(16));
|
||||
String bodyStr = TmPayUtil.getAsciiSort(body);
|
||||
String sign = TmPayUtil.getMD5Code(bodyStr+"&key="+SIGNKEY).toUpperCase();
|
||||
body.put("sign", sign);
|
||||
|
||||
HashMap<String, String> headerData = new HashMap<String, String>();
|
||||
headerData.put("Content-Type", "application/json");
|
||||
|
||||
String response = HttpUtils.sendPostWithHeaders(HTTP + url, headerData,JSON.toJSONString(body));
|
||||
System.out.println("API Response: " + response);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.ruoyi.common.pay.tm;
|
||||
|
||||
public interface IChannelInfo {
|
||||
|
||||
String getDeveloperId();
|
||||
|
||||
String getShopId();
|
||||
|
||||
String getHttpUrl();
|
||||
|
||||
String getSignKey();
|
||||
|
||||
String getNotifyUrl();
|
||||
|
||||
String getSn();
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
package com.ruoyi.common.pay.tm;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.pay.tm.config.TmPayConfig;
|
||||
import com.ruoyi.common.pay.tm.vo.RefundInfo;
|
||||
import com.ruoyi.common.pay.tm.vo.TmTradeInfo;
|
||||
import com.ruoyi.common.pay.wx.domain.Payable;
|
||||
import com.ruoyi.common.pay.wx.domain.RefundAble;
|
||||
import com.ruoyi.common.utils.http.AliHttpUtils;
|
||||
import com.ruoyi.common.utils.http.HttpUtils;
|
||||
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 太米支付
|
||||
*/
|
||||
@Service
|
||||
public class TmPayService {
|
||||
|
||||
private final static String SIGNKEY = "b4ixpiogfj5vu3tbkv23gj0dvo2j2ksz";
|
||||
|
||||
@Autowired
|
||||
private TmPayConfig config;
|
||||
|
||||
/**
|
||||
* 订单查询
|
||||
*/
|
||||
public TmTradeInfo orderQuery(String outTradeNo) {
|
||||
HashMap<String, Object> body = new HashMap<>();
|
||||
body.put("outTradeId", outTradeNo);
|
||||
body.put("terminalType", "1");
|
||||
body.put("shopId", config.getShopId());
|
||||
String response = doPost(config.getHttpUrl() + "/open/Pay/orderQuery", body, config);
|
||||
if (com.ruoyi.common.utils.StringUtils.hasText(response)) {
|
||||
JSONObject jsonResponse = JSON.parseObject(response);
|
||||
if (jsonResponse.getInteger("errCode") == 0 && "ok".equals(jsonResponse.getString("errMsg"))) {
|
||||
JSONObject tradeInfo = jsonResponse.getJSONObject("tradeInfo");
|
||||
TmTradeInfo tmTradeInfo = tradeInfo.toJavaObject(TmTradeInfo.class);
|
||||
return tmTradeInfo;
|
||||
} else {
|
||||
throw new ServiceException("订单查询失败: " + jsonResponse.getString("errMsg"));
|
||||
}
|
||||
}
|
||||
throw new ServiceException("订单查询数据为空");
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款
|
||||
*/
|
||||
public RefundInfo refund(RefundAble refundAble) {
|
||||
HashMap<String, Object> body = new HashMap<>();
|
||||
body.put("refundFee", String.valueOf(refundAble.refundAmount()));
|
||||
body.put("terminalType", "1");
|
||||
body.put("outTradeId", refundAble.refundOutTradeNo());
|
||||
body.put("shopId", config.getShopId());
|
||||
String response = doPost(config.getHttpUrl() + "/open/Pay/refund", body, config);
|
||||
if (com.ruoyi.common.utils.StringUtils.hasText(response)) {
|
||||
JSONObject jsonResponse = JSON.parseObject(response);
|
||||
if (jsonResponse.getInteger("errCode") == 0 && "退款成功".equals(jsonResponse.getString("errMsg"))) {
|
||||
JSONObject tradeInfo = jsonResponse.getJSONObject("refundInfo");
|
||||
RefundInfo refundInfo = tradeInfo.toJavaObject(RefundInfo.class);
|
||||
return refundInfo;
|
||||
} else {
|
||||
throw new ServiceException("退款失败: " + jsonResponse.getString("errMsg"));
|
||||
}
|
||||
}
|
||||
throw new ServiceException("退款数据为空");
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭订单
|
||||
*/
|
||||
public void closeOrder(String outTradeNo) {
|
||||
HashMap<String, Object> body = new HashMap<>();
|
||||
body.put("outTradeId", outTradeNo);
|
||||
body.put("terminalType", "1");
|
||||
body.put("shopId", config.getShopId());
|
||||
doPost(config.getHttpUrl() + "/open/Pay/orderClose", body, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序支付
|
||||
*/
|
||||
public PrepayWithRequestPaymentResponse pay(Payable payable) {
|
||||
HashMap<String, Object> body = new HashMap<>();
|
||||
body.put("payAmount", String.valueOf(payable.payableMoney()));
|
||||
body.put("terminalType", "1");
|
||||
body.put("shopId", config.getShopId()); // 从渠道获取shopId
|
||||
body.put("sn", config.getSn());
|
||||
body.put("payType", "wx_pay");
|
||||
body.put("outTradeId", payable.payableOutTradeNo());
|
||||
body.put("body", payable.payableDescription());
|
||||
body.put("notifyUrl", config.getNotifyUrl());
|
||||
body.put("openid", payable.payableOpenId());
|
||||
String response = doPost(config.getHttpUrl() + "/open/Pay/miniPay", body, config);
|
||||
|
||||
if (com.ruoyi.common.utils.StringUtils.hasText(response)) {
|
||||
// 解析 response,提取 "params" 字段
|
||||
JSONObject jsonResponse = JSON.parseObject(response);
|
||||
if (jsonResponse.getInteger("errCode") == 0 && "ok".equals(jsonResponse.getString("errMsg"))) {
|
||||
// 提取 "params" 并转成 PrepayWithRequestPaymentResponse 对象
|
||||
JSONObject params = jsonResponse.getJSONObject("params");
|
||||
PrepayWithRequestPaymentResponse paymentResponse = params.toJavaObject(PrepayWithRequestPaymentResponse.class);
|
||||
return paymentResponse;
|
||||
} else {
|
||||
throw new ServiceException("支付失败: " + jsonResponse.getString("errMsg"));
|
||||
}
|
||||
}
|
||||
throw new ServiceException("支付数据为空");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static String doPost(String url, HashMap<String, Object> body, TmPayConfig config) {
|
||||
body.put("developerId", config.getDeveloperId());
|
||||
body.put("version", "1.0");
|
||||
body.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
|
||||
body.put("nonceStr", TmPayUtil.getRandomString(16));
|
||||
|
||||
String bodyStr = TmPayUtil.getAsciiSort(body);
|
||||
String sign = TmPayUtil.getMD5Code(bodyStr + "&key=" + config.getSignKey()).toUpperCase();
|
||||
body.put("sign", sign);
|
||||
|
||||
HashMap<String, String> headerData = new HashMap<>();
|
||||
headerData.put("Content-Type", "application/json");
|
||||
|
||||
String response = HttpUtils.sendPostWithHeaders(url, headerData, JSON.toJSONString(body));
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public boolean validSign(Map<String, Object> params) {
|
||||
// 获取传递过来的签名
|
||||
String receivedSign = (String)params.get("sign");
|
||||
// 获取签名字段
|
||||
System.out.println("获取到的签名-------------:"+receivedSign);
|
||||
if (receivedSign == null) {
|
||||
return false; // 如果没有传递签名,验签失败
|
||||
}
|
||||
// 移除签名字段后,重新生成签名
|
||||
params.remove("sign");
|
||||
// 按照请求时的签名逻辑,生成签名字符串
|
||||
String paramsStr = TmPayUtil.getAsciiSort(params); // 按ASCII排序
|
||||
String generatedSign = TmPayUtil.getMD5Code(paramsStr + "&key=" + SIGNKEY).toUpperCase(); // 重新生成签名
|
||||
System.out.println("新生成的签名-----------:"+generatedSign);
|
||||
// 比较签名是否一致
|
||||
return generatedSign.equals(receivedSign);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
package com.ruoyi.common.pay.tm;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class TmPayUtil {
|
||||
// 全局数组
|
||||
private final static String[] strDigits = { "0", "1", "2", "3", "4", "5",
|
||||
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
|
||||
|
||||
private static Random random = null;
|
||||
|
||||
// 返回形式为数字跟字符串
|
||||
public static String byteToArrayString(byte bByte) {
|
||||
int iRet = bByte;
|
||||
if (iRet < 0) {
|
||||
iRet += 256;
|
||||
}
|
||||
int iD1 = iRet / 16;
|
||||
int iD2 = iRet % 16;
|
||||
return strDigits[iD1] + strDigits[iD2];
|
||||
}
|
||||
|
||||
// 返回形式只为数字
|
||||
public static String byteToNum(byte bByte) {
|
||||
int iRet = bByte;
|
||||
if (iRet < 0) {
|
||||
iRet += 256;
|
||||
}
|
||||
return String.valueOf(iRet);
|
||||
}
|
||||
|
||||
// 转换字节数组为16进制字串
|
||||
public static String byteToString(byte[] bByte) {
|
||||
StringBuffer sBuffer = new StringBuffer();
|
||||
for (int i = 0; i < bByte.length; i++) {
|
||||
sBuffer.append(byteToArrayString(bByte[i]));
|
||||
}
|
||||
return sBuffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* md5 加密
|
||||
* @param strObj
|
||||
* @return
|
||||
*/
|
||||
public static String getMD5Code(String strObj) {
|
||||
String resultString = null;
|
||||
try {
|
||||
resultString = new String(strObj);
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
// md.digest() 该函数返回值为存放哈希值结果的byte数组
|
||||
resultString = byteToString(md.digest(strObj.getBytes()));
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return resultString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取随机数
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
public static String getRandomString(int length) {
|
||||
// 定义一个字符串(A-Z,a-z1-9)即62位;
|
||||
String str = "zxcvbnmlkjhgfdsaqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
|
||||
// 由Random生成随机数
|
||||
if (random == null) {
|
||||
random = new Random();
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
// 长度为几就循环几次
|
||||
for (int i = 0; i < length; ++i) {
|
||||
// 产生0-61的数字
|
||||
int number = random.nextInt(62);
|
||||
// 将产生的数字通过length次承载到sb中
|
||||
sb.append(str.charAt(number));
|
||||
}
|
||||
// 将承载的字符转换成字符串
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getUUID() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public static String getUUIDNoLine() {
|
||||
String s = UUID.randomUUID().toString();
|
||||
return s.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24);
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数名ASCII码从小到大排序(字典序)
|
||||
* @param map
|
||||
* @return
|
||||
*/
|
||||
public static String getAsciiSort(Map<String, Object> map) {
|
||||
List<Entry<String, Object>> infoIds = new ArrayList<>(map.entrySet());
|
||||
|
||||
// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
|
||||
Collections.sort(infoIds, new Comparator<Entry<String, Object>>() {
|
||||
public int compare(Entry<String, Object> o1, Entry<String, Object> o2) {
|
||||
return o1.getKey().compareToIgnoreCase(o2.getKey());
|
||||
}
|
||||
});
|
||||
|
||||
// 构造签名键值对的格式
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Entry<String, Object> item : infoIds) {
|
||||
String key = item.getKey();
|
||||
Object val = item.getValue();
|
||||
|
||||
if (key != null && !key.isEmpty() && val != null) {
|
||||
sb.append(key).append("=").append(val.toString()).append("&");
|
||||
}
|
||||
}
|
||||
|
||||
if (sb.length() > 0 && sb.charAt(sb.length() - 1) == '&') {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.ruoyi.common.pay.tm.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
* 2024/9/30
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "tm")
|
||||
@Data
|
||||
public class TmPayConfig {
|
||||
// 开发者ID
|
||||
private String developerId;
|
||||
|
||||
// 门店ID
|
||||
private String shopId;
|
||||
|
||||
// 签名Key
|
||||
private String signKey;
|
||||
|
||||
// API地址
|
||||
private String httpUrl;
|
||||
|
||||
// 收银设备SN
|
||||
private String sn;
|
||||
|
||||
// 通知地址
|
||||
private String notifyUrl;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package com.ruoyi.common.pay.tm.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 交易结果
|
||||
* @author qzz
|
||||
* 2024/9/24
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PayStatus {
|
||||
|
||||
SUCCESS("SUCCESS", "支付成功"),
|
||||
NOTPAY("NOTPAY", "未支付"),
|
||||
CLOSE("CLOSE", "已关闭"),
|
||||
REVOKED("REVOKED", "已撤销"),
|
||||
USERPAYING("USERPAYING", "用户支付中"),
|
||||
PAYERROR("PAYERROR", "支付失败"),
|
||||
REFUND("REFUND", "转入退款"),
|
||||
OPERATE_SUCCESS("OPERATE_SUCCESS", "预授权请求操作成功"),
|
||||
OPERATE_FAIL("OPERATE_FAIL", "预授权请求操作失败"),
|
||||
OPERATE_SETTLING("OPERATE_SETTLING", "押金消费已受理"),
|
||||
REVOKED_SUCCESS("REVOKED_SUCCESS", "预授权请求撤销成功");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 判断支付状态是否成功
|
||||
* @param code 支付状态码
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean isSuccess(String code) {
|
||||
return SUCCESS.getCode().equals(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断支付状态是否转入退款
|
||||
* @param code 支付状态码
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean isRefund(String code) {
|
||||
return REFUND.getCode().equals(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据状态码获取对应的支付状态
|
||||
* @param code 支付状态码
|
||||
* @return 对应的PayStatus枚举
|
||||
*/
|
||||
public static PayStatus getByCode(String code) {
|
||||
for (PayStatus status : PayStatus.values()) {
|
||||
if (status.getCode().equals(code)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.ruoyi.common.pay.tm.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 退款状态枚举
|
||||
* @author
|
||||
* 2024/09/24
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum RefundStatus {
|
||||
FAILED(0, "退款失败"),
|
||||
REFUNDED(1, "转入退款");
|
||||
|
||||
private final int code;
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 根据状态码判断是否退款成功
|
||||
* @param code 状态码
|
||||
* @return 是否退款成功
|
||||
*/
|
||||
public static boolean isSuccess(int code) {
|
||||
return REFUNDED.getCode() == code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据状态码获取对应的退款状态枚举
|
||||
* @param code 状态码
|
||||
* @return 对应的 RefundStatus 枚举
|
||||
*/
|
||||
public static RefundStatus getByCode(int code) {
|
||||
for (RefundStatus status : RefundStatus.values()) {
|
||||
if (status.getCode() == code) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.ruoyi.common.pay.tm.vo;
|
||||
|
||||
import com.ruoyi.common.pay.tm.enums.RefundStatus;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 退款信息对象
|
||||
* @author
|
||||
* 2024/09/24
|
||||
*/
|
||||
@Data
|
||||
public class RefundInfo {
|
||||
private String id; // 太米系统退款记录Id
|
||||
private Long tradeInfoId; // 太米系统流水Id
|
||||
private String module; // 模块: pay, mall, recharge, become_member, eatIn, takeOut, selfTake, payment_card, times_card
|
||||
private String remark; // 备注
|
||||
private Long merchantId; // 品牌Id
|
||||
private String shopId; // 门店Id
|
||||
private Long merchantUserId; // 门店员工Id
|
||||
private Long codeId; // 款台码Id
|
||||
private Long memberInfoId; // 会员Id
|
||||
private Long workRecordId; // 交班记录Id
|
||||
private String fromType; // 订单来源: wx, alipay, web, mini, pos, pc, desktop, api
|
||||
private String refundTime; // 退款成功时间
|
||||
private String refundMessage; // 退款失败原因
|
||||
private RefundStatus refundStatus; // 退款状态: RefundStatus 枚举
|
||||
private String refundAmount; // 已退款金额
|
||||
private String createTime; // 创建时间
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
package com.ruoyi.common.pay.tm.vo;
|
||||
|
||||
import com.ruoyi.common.pay.tm.enums.PayStatus;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TmTradeInfo {
|
||||
|
||||
/**
|
||||
* 太米系统流水Id
|
||||
*/
|
||||
private Integer id;
|
||||
|
||||
/**
|
||||
* 第三方内部流水号
|
||||
*/
|
||||
private String outTradeId;
|
||||
|
||||
/**
|
||||
* 微信或支付宝或银联的订单号。个别支付渠道可能无此参数
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 太米商户订单号
|
||||
*/
|
||||
private String orderNum;
|
||||
|
||||
/**
|
||||
* 支付凭证条码,个别支付渠道可能返回空
|
||||
*/
|
||||
private String barCode;
|
||||
|
||||
/**
|
||||
* 模块:pay:收银;mall:优选卡券货架;recharge:充值;
|
||||
* become_member:会员购买;eatIn:店内下单;
|
||||
* takeOut:外送订单;selfTake:预约自取;
|
||||
* payment_card:付费卡券;times_card:次/月卡
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 品牌Id
|
||||
*/
|
||||
private Integer merchantId;
|
||||
|
||||
/**
|
||||
* 门店Id
|
||||
*/
|
||||
private Integer shopId;
|
||||
|
||||
/**
|
||||
* 门店员工id
|
||||
*/
|
||||
private Integer merchantUserId;
|
||||
|
||||
/**
|
||||
* 款台码Id
|
||||
*/
|
||||
private Integer codeId;
|
||||
|
||||
/**
|
||||
* 会员Id
|
||||
*/
|
||||
private Integer memberInfoId;
|
||||
|
||||
/**
|
||||
* 交班记录id
|
||||
*/
|
||||
private Integer workRecordId;
|
||||
|
||||
/**
|
||||
* 订单来源:wx:微信收款码;alipay:支付宝收款码;
|
||||
* web:Web页面;mini:小程序;pos:POS机;
|
||||
* pc:PC;desktop:台式消费机;api:开放平台接口
|
||||
*/
|
||||
private String fromType;
|
||||
|
||||
/**
|
||||
* 订单总额,单位分
|
||||
*/
|
||||
private Integer orderAmount;
|
||||
|
||||
/**
|
||||
* 会员优惠,单位分
|
||||
*/
|
||||
private Integer memberCoupon;
|
||||
|
||||
/**
|
||||
* 活动优惠,单位分
|
||||
*/
|
||||
private Integer activityCoupon;
|
||||
|
||||
/**
|
||||
* 卡券优惠,单位分
|
||||
*/
|
||||
private Integer cardCoupon;
|
||||
|
||||
/**
|
||||
* 积分抵扣,单位分
|
||||
*/
|
||||
private Integer pointDeduction;
|
||||
|
||||
/**
|
||||
* 实收金额,单位分
|
||||
*/
|
||||
private Integer incomeAmount;
|
||||
|
||||
/**
|
||||
* 支付方式:wx_pay:微信支付;ali_pay:支付宝;
|
||||
* union_offline:银行卡;union_qrcode:银联扫码;
|
||||
* union_online:银联钱包;member_wallet:会员钱包;cash:现金
|
||||
*/
|
||||
private String payType;
|
||||
|
||||
/**
|
||||
* 1、已支付 0、未支付
|
||||
*/
|
||||
private Integer isPaid;
|
||||
|
||||
/**
|
||||
* 支付状态:
|
||||
* SUCCESS:支付成功;NOTPAY:未支付;CLOSE:已关闭;
|
||||
* REVOKED:已撤销;USERPAYING:用户支付中;
|
||||
* PAYERROR:支付失败;REFUND:转入退款;
|
||||
* OPERATE_SUCCESS:预授权请求操作成功、OPERATE_FAIL:预授权请求操作失败;
|
||||
* OPERATE_SETTLING:押金消费已受理;
|
||||
* REVOKED_SUCCESS:预授权请求撤销成功
|
||||
*/
|
||||
private PayStatus payStatus;
|
||||
|
||||
/**
|
||||
* 支付时间
|
||||
*/
|
||||
private String payTime;
|
||||
|
||||
/**
|
||||
* 支付失败原因
|
||||
*/
|
||||
private String payError;
|
||||
|
||||
/**
|
||||
* 退款状态:0表示未退款,1表示转入退款
|
||||
*/
|
||||
private Integer refundStatus;
|
||||
|
||||
/**
|
||||
* 已退款金额,单位分
|
||||
*/
|
||||
private String refundAmount;
|
||||
|
||||
/**
|
||||
* 终端sn号
|
||||
*/
|
||||
private String sn;
|
||||
|
||||
/**
|
||||
* 应结订单金额(微信代金券用)
|
||||
*/
|
||||
private String settlementTotalFee;
|
||||
|
||||
/**
|
||||
* 0 : 普通订单 1:刷脸设备刷脸支付 2 : 刷脸设备扫码支付 3 : 刷脸设备会员钱包支付
|
||||
*/
|
||||
private String isFromFacePay;
|
||||
|
||||
/**
|
||||
* 总代金券金额(微信代金券用)
|
||||
*/
|
||||
private String couponFee;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private String createTime;
|
||||
}
|
|
@ -11,6 +11,7 @@ import java.net.URL;
|
|||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Map;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
@ -430,4 +431,64 @@ public class HttpUtils
|
|||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 向指定 URL 发送 POST 方法的请求,并支持自定义请求头和 JSON 请求体
|
||||
*
|
||||
* @param url 发送请求的 URL
|
||||
* @param headerData 请求头信息,键值对形式
|
||||
* @param body 请求体,通常为 JSON 格式的字符串
|
||||
* @return 所代表远程资源的响应结果
|
||||
*/
|
||||
public static String sendPostWithHeaders(String url, Map<String, String> headerData, String body) {
|
||||
log.info("body------- - {}", body);
|
||||
PrintWriter out = null;
|
||||
BufferedReader in = null;
|
||||
StringBuilder result = new StringBuilder();
|
||||
try {
|
||||
log.info("sendPostWithHeaders - {}", url);
|
||||
URL realUrl = new URL(url);
|
||||
URLConnection conn = realUrl.openConnection();
|
||||
|
||||
// 设置请求头信息
|
||||
for (Map.Entry<String, String> entry : headerData.entrySet()) {
|
||||
conn.setRequestProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
// 发送 POST 请求体数据
|
||||
out = new PrintWriter(conn.getOutputStream());
|
||||
out.print(body); // 发送 JSON 格式的 body
|
||||
out.flush();
|
||||
|
||||
// 读取响应数据
|
||||
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
result.append(line);
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
} catch (ConnectException e) {
|
||||
log.error("调用HttpUtils.sendPostWithHeaders ConnectException, url=" + url + ", body=" + body, e);
|
||||
} catch (SocketTimeoutException e) {
|
||||
log.error("调用HttpUtils.sendPostWithHeaders SocketTimeoutException, url=" + url + ", body=" + body, e);
|
||||
} catch (IOException e) {
|
||||
log.error("调用HttpUtils.sendPostWithHeaders IOException, url=" + url + ", body=" + body, e);
|
||||
} catch (Exception e) {
|
||||
log.error("调用HttpUtils.sendPostWithHeaders Exception, url=" + url + ", body=" + body, e);
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log.error("调用in.close Exception, url=" + url + ", body=" + body, ex);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import com.ruoyi.ss.mchApply.domain.enums.MchApplyStatus;
|
|||
import com.ruoyi.ss.mchApply.service.IMchApplyService;
|
||||
import com.ruoyi.ss.receiveBill.domain.ReceiveBillQuery;
|
||||
import com.ruoyi.ss.receiveBill.domain.enums.ReceiveBillGroupBy;
|
||||
import com.ruoyi.ss.receiveBill.domain.enums.ReceiveBillType;
|
||||
import com.ruoyi.ss.receiveBill.domain.vo.ReceiveAmountVO;
|
||||
import com.ruoyi.ss.receiveBill.service.ReceiveBillService;
|
||||
import com.ruoyi.ss.storeApply.domain.StoreApplyQuery;
|
||||
|
@ -115,6 +116,7 @@ public class DashboardService {
|
|||
|
||||
// 查询月费收入
|
||||
ReceiveBillQuery receiveQuery = new ReceiveBillQuery();
|
||||
receiveQuery.setType(ReceiveBillType.MONTH.getType());
|
||||
receiveQuery.setStartDate(query.getStartDate());
|
||||
receiveQuery.setEndDate(query.getEndDate());
|
||||
List<ReceiveAmountVO<Date>> receiveList = receiveBillService.selectCommonSumOfAmount(receiveQuery, ReceiveBillGroupBy.create_date.name());
|
||||
|
|
|
@ -11,7 +11,8 @@ import lombok.Getter;
|
|||
@AllArgsConstructor
|
||||
public enum ReceiveBillType {
|
||||
|
||||
MONTH("1", "月费");
|
||||
MONTH("1", "月费"),
|
||||
OTHER("2", "其他");
|
||||
|
||||
private final String type;
|
||||
|
||||
|
|
|
@ -155,4 +155,9 @@ public class Store extends BaseEntity
|
|||
@ApiModelProperty("是否生效")
|
||||
@JsonView(JsonViewProfile.App.class)
|
||||
private Boolean enabled;
|
||||
|
||||
@Excel(name = "是否允许营业时间外使用")
|
||||
@ApiModelProperty("是否允许营业时间外使用")
|
||||
@JsonView(JsonViewProfile.App.class)
|
||||
private Boolean useOutTime;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.ruoyi.ss.store.domain;
|
||||
|
||||
import com.ruoyi.ss.store.domain.enums.StoreStatus;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -99,6 +100,8 @@ public class StoreBO extends Store {
|
|||
bo.setContactName(getContactName());
|
||||
bo.setContactMobile(getContactMobile());
|
||||
bo.setShow(getShow());
|
||||
bo.setStatus(StoreStatus.NORMAL.getStatus());
|
||||
bo.setEnabled(true);
|
||||
return bo;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
ss.show,
|
||||
ss.status,
|
||||
ss.enabled,
|
||||
ss.use_out_time,
|
||||
su.user_name as user_name
|
||||
from sm_store ss
|
||||
left join sm_user su on su.user_id = ss.user_id
|
||||
|
@ -51,6 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="query.userName != null "> and su.user_name like concat('%', #{query.userName}, '%')</if>
|
||||
<if test="query.status != null "> and ss.status = #{query.status}</if>
|
||||
<if test="query.enabled != null "> and ss.enabled = #{query.enabled}</if>
|
||||
<if test="query.useOutTime != null "> and ss.use_out_time = #{query.useOutTime}</if>
|
||||
<if test="query.keyword != null and query.keyword != ''">
|
||||
and (
|
||||
ss.name like concat('%', #{query.keyword}, '%')
|
||||
|
@ -158,6 +160,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="show != null">`show`,</if>
|
||||
<if test="status != null">`status`,</if>
|
||||
<if test="enabled != null">`enabled`,</if>
|
||||
<if test="useOutTime != null">use_out_time,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="name != null">#{name},</if>
|
||||
|
@ -181,6 +184,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="show != null">#{show},</if>
|
||||
<if test="status != null">#{status},</if>
|
||||
<if test="enabled != null">#{enabled},</if>
|
||||
<if test="useOutTime != null">#{useOutTime},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
|
@ -213,6 +217,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="data.show != null">`show` = #{data.show},</if>
|
||||
<if test="data.status != null">`status` = #{data.status},</if>
|
||||
<if test="data.enabled != null">`enabled` = #{data.enabled},</if>
|
||||
<if test="data.useOutTime != null">use_out_time = #{data.useOutTime},</if>
|
||||
</sql>
|
||||
|
||||
<update id="updateByQuery">
|
||||
|
|
|
@ -2,7 +2,9 @@ package com.ruoyi.ss.store.service;
|
|||
|
||||
import com.ruoyi.common.core.domain.ValidateResult;
|
||||
import com.ruoyi.ss.store.domain.Store;
|
||||
import com.ruoyi.ss.store.domain.StoreVo;
|
||||
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -91,4 +93,9 @@ public interface StoreValidator {
|
|||
* 是否正在审核中
|
||||
*/
|
||||
boolean hasApproving(List<Long> ids);
|
||||
|
||||
/**
|
||||
* 判断是否在营业时间内
|
||||
*/
|
||||
boolean isBusinessTime(StoreVo store, LocalTime time);
|
||||
}
|
||||
|
|
|
@ -301,6 +301,14 @@ public class StoreValidatorImpl extends BaseValidator implements StoreValidator
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBusinessTime(StoreVo store, LocalTime time) {
|
||||
if (store.getUseOutTime() == null || store.getUseOutTime()) {
|
||||
return true;
|
||||
}
|
||||
return time.isAfter(store.getBusinessTimeStart()) && time.isBefore(store.getBusinessTimeEnd());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验时间是符合规则
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.ruoyi.ss.device.domain.enums.DeviceOnlineStatus;
|
|||
import com.ruoyi.ss.device.domain.vo.DeviceVO;
|
||||
import com.ruoyi.ss.device.service.DeviceService;
|
||||
import com.ruoyi.ss.model.domain.enums.ModelTag;
|
||||
import com.ruoyi.ss.store.domain.StoreVo;
|
||||
import com.ruoyi.ss.store.service.StoreValidator;
|
||||
import com.ruoyi.ss.suit.domain.SuitVO;
|
||||
import com.ruoyi.ss.suit.domain.enums.SuitFeeMode;
|
||||
|
@ -32,6 +33,7 @@ import org.springframework.stereotype.Service;
|
|||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -64,9 +66,17 @@ public class TransactionBillValidatorImpl extends BaseValidator implements Trans
|
|||
|
||||
RechargeDTO dto = bo.getParams();
|
||||
|
||||
// 门店
|
||||
StoreVo store = bo.getStore();
|
||||
if (store != null) {
|
||||
LocalTime now = LocalTime.now();
|
||||
if (!storeValidator.isBusinessTime(store, now)) {
|
||||
return error(String.format("当前店铺不在营业时间内,无法下单。营业时间:%s - %s", store.getBusinessTimeStart(), store.getBusinessTimeEnd()));
|
||||
}
|
||||
}
|
||||
|
||||
// 用户
|
||||
SmUserVo user = bo.getUser();
|
||||
ServiceUtil.assertion(user == null, "用户不存在");
|
||||
|
||||
// 检查用户是否有未支付的智能分时段订单
|
||||
if (this.hasUnpaidSmartTimingOrder(user.getUserId())) {
|
||||
|
@ -98,11 +108,6 @@ public class TransactionBillValidatorImpl extends BaseValidator implements Trans
|
|||
return error("该设备有正在使用中的订单,暂时无法下单");
|
||||
}
|
||||
|
||||
// 店铺
|
||||
if (!storeValidator.isExist(Collections.singletonList(device.getStoreId()))) {
|
||||
return error("当前设备店铺不存在,无法充值");
|
||||
}
|
||||
|
||||
// 商户检查
|
||||
if (!userValidator.isUsage(device.getUserId())) {
|
||||
return error("当前设备商户不存在或不可用,无法充值,请确认商户账号是否正常");
|
||||
|
|
|
@ -91,7 +91,7 @@ public class UserAssemblerImpl implements UserAssembler {
|
|||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
// TODO 优先获取用户的延迟到账时间
|
||||
// 优先获取用户的延迟到账时间
|
||||
if (user.getArrivalDelay() != null) {
|
||||
user.setRealArrivalDelay(user.getArrivalDelay());
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue
Block a user