Commit 538f83d0 by wuwenlong

create order dev;

parent 81caf6da
......@@ -19,6 +19,9 @@ public class WeChatConfig
private String appSecret;
private String mchId;
private String signKey;
private String publicAppId;
......
......@@ -9,6 +9,10 @@ import io.jsonwebtoken.Claims;
*/
public class Constants
{
public static final String CONFIG_KEY_SITE_URL = "site_url"; //域名
public static final String CONFIG_KEY_API_URL = "api_url"; //admin接口地址
// 订单取消Key
public static final String ORDER_AUTO_CANCEL_KEY = "order_auto_cancel_key";
//测试环境,经测试需要,手机验证码测试环境可以不需要就能登录,value为1,则不验证验证码
public static final String PHONE_TEST_KEY = "phone_test_key";
//通过uuid缓存的key 查找对应的 token数据 WX_TOKEN_USER:TOKEN
......
package share.common.enums;
/**
* @Author wwl
* @Date 2023/10/20 10:57
*/
public enum OrderTypeEnum {
RESERVER(1,"预定"),
RENEW(2,"续费"),
RECHARGE(3,"充值");
private Integer code;
private String name;
OrderTypeEnum(Integer code, String name) {
this.code = code;
this.name = name;
}
public static OrderTypeEnum getEnumByCode(Integer code){
for (OrderTypeEnum type : OrderTypeEnum.values()) {
if (type.code.compareTo(code)==0) {
return type;
}
}
return null;
}
public Integer getCode(){
return code;
}
public String getName() {
return name;
}
}
......@@ -18,6 +18,7 @@ import share.system.domain.vo.MyRecord;
import share.system.request.CreateOrderRequest;
import share.system.request.OrderComputedPriceRequest;
import share.system.response.ComputedOrderPriceResponse;
import share.system.response.OrderPayResultResponse;
import share.system.service.ISOrderService;
import share.common.core.page.TableDataInfo;
......@@ -63,17 +64,17 @@ public class SOrderController extends BaseController
/**
* 下单
*/
//@ApiOperation(value = "下单")
@ApiOperation(value = "下单")
@RequestMapping(value = "/creat", method = RequestMethod.POST)
public R<MyRecord> createOrder(@RequestBody @Validated CreateOrderRequest request) {
public R<OrderPayResultResponse> createOrder(@RequestBody @Validated CreateOrderRequest request) {
if("1".equals(redisUtil.frontInOutLogSwitch())) {
log.info("SOrderController method preOrder 入参 {}", JsonConvertUtil.write2JsonStr(request));
}
MyRecord record = sOrderService.createOrder(request);
OrderPayResultResponse response = sOrderService.createOrder(request);
if("1".equals(redisUtil.frontInOutLogSwitch())) {
log.info("SOrderController method preOrder 出参 {}", JsonConvertUtil.write2JsonStr(record));
log.info("SOrderController method preOrder 出参 {}", JsonConvertUtil.write2JsonStr(response));
}
return R.ok(record);
return R.ok(response);
}
......
......@@ -18,7 +18,8 @@ ruoyi:
wechat:
appId: wx0802dd3453f72cc9
appSecret: 0f36e45668b4cc714affc4fa6ecdb4bf
mchId: 0
signKey: 0
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
......
......@@ -18,7 +18,8 @@ ruoyi:
wechat:
appId: wx0802dd3453f72cc9
appSecret: 0f36e45668b4cc714affc4fa6ecdb4bf
mchId: 0
signKey: 0
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
......
......@@ -22,30 +22,22 @@ import javax.validation.constraints.NotBlank;
* @date 2023-10-09
*/
@Data
public class SOrder extends BaseEntity {
public class SOrder extends BaseEntity
{
private static final long serialVersionUID = 1L;
/**
* 订单ID
*/
/** 订单ID */
private Long id;
/**
* 订单流水号
*/
/** 订单流水号 */
@Excel(name = "订单编号")
private String orderNo;
/**
* 订单流水号
*/
@Excel(name = "订单编号")
@Excel(name = "商户订单号")
private String outTradeNo;
/**
* 订单类型(0:订房订单,1:续房订单,2:充值订单)
*/
@Excel(name = "订单类型(0:订房订单,1:续房订单,2:充值订单)")
/** 订单类型(0:订房订单,1:续房订单,2:充值订单) */
@Excel(name = "订单类型(1:订房订单,2:续房订单,3:充值订单)")
private Integer orderType;
@Excel(name = "支付类型(1:微信,2:支付宝)")
......@@ -60,21 +52,15 @@ public class SOrder extends BaseEntity {
@Excel(name = "房间ID")
private Long roomId;
/**
* 用户ID
*/
/** 用户ID */
@Excel(name = "用户ID")
private Long consumerId;
/**
* 用户名称
*/
/** 用户名称 */
@Excel(name = "用户名称")
private String consumerName;
/**
* 用户手机号
*/
/** 用户手机号 */
@Excel(name = "用户手机号")
private String consumerPhone;
......@@ -84,9 +70,7 @@ public class SOrder extends BaseEntity {
@ApiModelProperty(value = "套餐金额")
private BigDecimal packPrice;
/**
* 优惠券id
*/
/** 优惠券id */
@Excel(name = "优惠券id")
private String couponId;
......@@ -105,37 +89,27 @@ public class SOrder extends BaseEntity {
@Excel(name = "订单时长(H)")
private String timeLong;
/**
* 预约开始时间
*/
/** 预约开始时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
@Excel(name = "预约开始时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm")
private Date preStartDate;
/**
* 预约结束时间
*/
/** 预约结束时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
@Excel(name = "预约结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm")
private Date preEndDate;
/**
* 开始时间
*/
/** 开始时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
@Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm")
private Date startDate;
/**
* 结束时间
*/
/** 结束时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
@Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm")
private Date endDate;
/**
* 订单状态(0:待使用/已预约,1:使用中,2:已使用,3:已取消预约/退款中)
*/
/** 订单状态(0:待使用/已预约,1:使用中,2:已使用,3:已取消预约/退款中) */
@Excel(name = "订单状态(0:待使用/已预约,1:使用中,2:已使用)")
private Integer status;
......
......@@ -50,9 +50,6 @@ public class CreateOrderRequestVo {
@ApiModelProperty(value = "订单总金额,单位为分", required = true)
private int total_fee;
@ApiModelProperty(value = "必须传正确的用户端IP,支持ipv4、ipv6格式,获取方式详见获取用户ip指引", required = true)
private String spbill_create_ip;
@ApiModelProperty(value = "订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则")
private String time_start;
......
package share.system.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 微信调起支付参数对象
*/
@Data
@ApiModel(value="WxPayJsResultVo对象", description="微信调起支付参数对象")
public class WxPayJsResultVo {
@ApiModelProperty(value = "微信分配的小程序ID")
private String appId;
@ApiModelProperty(value = "随机字符串,不长于32位")
private String nonceStr;
@ApiModelProperty(value = "统一下单接口返回的 prepay_id 参数值")
private String packages;
@ApiModelProperty(value = "签名类型,默认为MD5,支持HMAC-SHA256和MD5。")
private String signType;
@ApiModelProperty(value = "时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间")
private String timeStamp;
@ApiModelProperty(value = "支付签名")
private String paySign;
@ApiModelProperty(value = "H5支付跳转链接")
private String mwebUrl;
@ApiModelProperty(value = "微信商户号")
private String partnerid;
@ApiModelProperty(value = "拉起收银台的ticket")
private String ticket;
}
......@@ -38,6 +38,9 @@ public class CreateOrderRequest {
@NotBlank(message = "订单类型不能为空")
private Integer orderType;
@ApiModelProperty(value = "续房订单的上一笔订单编号", required = true)
private String preOrderNo;
@ApiModelProperty(value = "购买方式(1:小时,2:套餐)", required = true)
@NotBlank(message = "购买方式不能为空")
private Integer buyType;
......
package share.system.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 支付订单参数
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="OrderPayRequest对象", description="订单支付")
public class OrderPayRequest {
@ApiModelProperty(value = "订单id")
private String uni;
@ApiModelProperty(value = "订单编号")
@NotNull(message = "订单编号不能为空")
private String orderNo;
@ApiModelProperty(value = "支付类型:weixin-微信支付,yue-余额支付,offline-线下支付,alipay-支付包支付")
@NotNull(message = "支付类型不能为空")
private String payType;
@ApiModelProperty(value = "支付渠道:weixinh5-微信H5支付,public-公众号支付,routine-小程序支付,weixinAppIos-微信appios支付,weixinAppAndroid-微信app安卓支付,alipay-支付包支付,appAliPay-App支付宝支付")
@NotNull(message = "支付渠道不能为空")
private String payChannel;
@ApiModelProperty(value = "支付平台")
private String from;
@ApiModelProperty(value = "下单时小程序的场景值")
private Integer scene;
}
package share.system.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import share.system.domain.vo.WxPayJsResultVo;
/**
* 订单支付结果 Response
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="OrderPayResultResponse对象", description="订单支付结果响应对象")
public class OrderPayResultResponse {
private static final long serialVersionUID=1L;
@ApiModelProperty(value = "支付状态")
private Boolean status;
@ApiModelProperty(value = "微信调起支付参数对象")
private WxPayJsResultVo jsConfig;
@ApiModelProperty(value = "支付类型")
private String payType;
@ApiModelProperty(value = "订单编号")
private String orderNo;
@ApiModelProperty(value = "微信支付回调的url")
private String notifyUrl;
}
......@@ -9,6 +9,7 @@ import share.system.domain.vo.MyRecord;
import share.system.request.CreateOrderRequest;
import share.system.request.OrderComputedPriceRequest;
import share.system.response.ComputedOrderPriceResponse;
import share.system.response.OrderPayResultResponse;
/**
* 订单Service接口
......@@ -80,7 +81,7 @@ public interface ISOrderService extends IService<SOrder>
* @param request 预下单请求参数
* @return PreOrderResponse
*/
MyRecord createOrder(CreateOrderRequest request);
OrderPayResultResponse createOrder(CreateOrderRequest request);
/**
* 计算订单价格
......
package share.system.service;
import share.system.domain.SOrder;
import share.system.request.OrderPayRequest;
import share.system.response.OrderPayResultResponse;
/**
* @Author wwl
* @Date 2023/10/23 15:46
*/
public interface OrderPayService {
/**
* 订单支付
* @param sOrder 支付参数
* @return OrderPayResultResponse
*/
OrderPayResultResponse payment(SOrder sOrder);
}
package share.system.service.impl;
import cn.hutool.core.util.ObjectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import share.common.config.WeChatConfig;
import share.common.constant.Constants;
import share.common.constant.PayConstants;
import share.common.enums.PayTypeEnum;
import share.common.enums.YesNoEnum;
import share.common.exception.base.BaseException;
import share.common.utils.BaseUtil;
import share.common.utils.DateUtil;
import share.common.utils.JsonConvertUtil;
import share.system.domain.SConsumerToken;
import share.system.domain.SOrder;
import share.system.domain.vo.CreateOrderRequestVo;
import share.system.domain.vo.CreateOrderResponseVo;
import share.system.domain.vo.WxPayJsResultVo;
import share.system.request.OrderPayRequest;
import share.system.response.OrderPayResultResponse;
import share.system.service.ISOrderService;
import share.system.service.OrderPayService;
import share.system.service.SConsumerTokenService;
import share.system.service.WechatNewService;
import share.system.util.WxPayUtil;
import java.math.BigDecimal;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Author wwl
* @Date 2023/10/23 15:47
*/
@Service
public class OrderPayServiceImpl implements OrderPayService {
private static final Logger logger = LoggerFactory.getLogger(OrderPayServiceImpl.class);
@Autowired
private WeChatConfig weChatConfig;
@Autowired
private SConsumerTokenService consumerTokenService;
@Autowired
private WechatNewService wechatNewService;
@Autowired
private ISOrderService orderService;
@Override
public OrderPayResultResponse payment(SOrder sOrder) {
OrderPayResultResponse response = new OrderPayResultResponse();
response.setOrderNo(sOrder.getOrderNo());
response.setPayType(PayTypeEnum.getEnumByCode(sOrder.getPayType()).getValue());
response.setStatus(YesNoEnum.no.getFlag());
ConcurrentHashMap<String, String> unifiedorder = unifiedorder(sOrder);
WxPayJsResultVo vo = new WxPayJsResultVo();
vo.setAppId(unifiedorder.get("appId"));
vo.setNonceStr(unifiedorder.get("nonceStr"));
vo.setPackages(unifiedorder.get("package"));
vo.setSignType(unifiedorder.get("signType"));
vo.setTimeStamp(unifiedorder.get("timeStamp"));
vo.setPaySign(unifiedorder.get("paySign"));
// 更新商户订单号
sOrder.setOutTradeNo(unifiedorder.get("outTradeNo"));
orderService.updateById(sOrder);
response.setJsConfig(vo);
return null;
}
/**
* 预下单
* @param sOrder 订单
* @return 预下单返回对象
*/
private ConcurrentHashMap<String, String> unifiedorder(SOrder sOrder) {
// 获取用户openId
SConsumerToken userToken = consumerTokenService.getTokenByUserId(sOrder.getConsumerId());
if (ObjectUtil.isNull(userToken)) {
throw new BaseException("该用户没有openId");
}
// 获取appid、mch_id
// 微信签名key
String appId = weChatConfig.getAppId();
String mchId = weChatConfig.getMchId();
String signKey = weChatConfig.getSignKey();
// 获取微信预下单对象
CreateOrderRequestVo unifiedorderVo = getUnifiedorderVo(sOrder, userToken.getToken(), appId, mchId, signKey);
// 预下单(统一下单)
CreateOrderResponseVo responseVo = wechatNewService.payUnifiedorder(unifiedorderVo);
logger.info("CreateOrderResponseVo :", JsonConvertUtil.write2JsonStr(responseVo));
// 组装前端预下单参数
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
map.put("appId", unifiedorderVo.getAppid());
map.put("nonceStr", unifiedorderVo.getAppid());
map.put("package", "prepay_id=".concat(responseVo.getPrepayId()));
map.put("signType", unifiedorderVo.getSign_type());
Long currentTimestamp = WxPayUtil.getCurrentTimestamp();
map.put("timeStamp", Long.toString(currentTimestamp));
String paySign = WxPayUtil.getSign(map, signKey);
map.put("paySign", paySign);
map.put("prepayId", responseVo.getPrepayId());
map.put("prepayTime", DateUtil.nowDateTimeStr());
map.put("outTradeNo", unifiedorderVo.getOut_trade_no());
return map;
}
/**
* 获取微信预下单对象
* @return 微信预下单对象
*/
private CreateOrderRequestVo getUnifiedorderVo(SOrder sOrder, String openid, String appId, String mchId, String signKey) {
// 获取域名
String apiDomain = Constants.CONFIG_KEY_API_URL;
CreateOrderRequestVo vo = new CreateOrderRequestVo();
vo.setAppid(appId);
vo.setMch_id(mchId);
vo.setNonce_str(WxPayUtil.getNonceStr());
vo.setSign_type(PayConstants.WX_PAY_SIGN_TYPE_MD5);
vo.setOut_trade_no(BaseUtil.getOrderNo("wxNo"));
// 订单中使用的是BigDecimal,这里要转为Integer类型
vo.setTotal_fee(sOrder.getPayPrice().multiply(BigDecimal.TEN).multiply(BigDecimal.TEN).intValue());
vo.setNotify_url(apiDomain + PayConstants.WX_PAY_NOTIFY_API_URI);
vo.setTrade_type(PayConstants.WX_PAY_TRADE_TYPE_JS);
vo.setOpenid(openid);
String sign = WxPayUtil.getSign(vo, signKey);
vo.setSign(sign);
return vo;
}
}
......@@ -4,6 +4,7 @@ import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import cn.hutool.core.collection.CollUtil;
......@@ -11,8 +12,14 @@ import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import share.common.constant.Constants;
import share.common.constant.PayConstants;
import share.common.core.redis.RedisUtil;
import share.common.enums.*;
import share.common.exception.base.BaseException;
import share.common.utils.BaseUtil;
......@@ -26,15 +33,14 @@ import share.system.domain.SConsumerCoupon;
import share.system.domain.SRoom;
import share.system.domain.vo.FrontTokenComponent;
import share.system.domain.vo.MyRecord;
import share.system.domain.vo.WxPayJsResultVo;
import share.system.mapper.SOrderMapper;
import share.system.domain.SOrder;
import share.system.request.CreateOrderRequest;
import share.system.request.OrderComputedPriceRequest;
import share.system.response.ComputedOrderPriceResponse;
import share.system.service.ISConsumerCouponService;
import share.system.service.ISOrderService;
import share.system.service.ISRoomService;
import share.system.service.SConsumerService;
import share.system.response.OrderPayResultResponse;
import share.system.service.*;
/**
* 订单Service业务层处理
......@@ -45,6 +51,10 @@ import share.system.service.SConsumerService;
@Service
public class SOrderServiceImpl extends ServiceImpl<SOrderMapper,SOrder> implements ISOrderService
{
private static final Logger logger = LoggerFactory.getLogger(SOrderServiceImpl.class);
@Autowired
private SOrderMapper sOrderMapper;
......@@ -57,6 +67,12 @@ public class SOrderServiceImpl extends ServiceImpl<SOrderMapper,SOrder> implemen
@Autowired
private ISRoomService roomService;
@Autowired
private OrderPayService orderPayService;
@Autowired
private RedisUtil redisUtil;
/**
* 查询订单
*
......@@ -145,7 +161,7 @@ public class SOrderServiceImpl extends ServiceImpl<SOrderMapper,SOrder> implemen
}
@Override
public MyRecord createOrder(CreateOrderRequest request) {
public OrderPayResultResponse createOrder(CreateOrderRequest request) {
SConsumer user = FrontTokenComponent.getWxSConsumerEntry();
if (ObjectUtil.isNull(user)) {
throw new BaseException("您的登录已过期,请先登录");
......@@ -154,8 +170,51 @@ public class SOrderServiceImpl extends ServiceImpl<SOrderMapper,SOrder> implemen
//校验订单金额
checkOrderPrice(sOrder,user);
//校验订单预约时间
checkOrderDate(request);
OrderPayResultResponse response = new OrderPayResultResponse();
if (request.getPayFee().compareTo(BigDecimal.ZERO) <= 0) {
response.setPayType(PayTypeEnum.WECHAT.getValue());
response.setStatus(YesNoEnum.yes.getFlag());
response.setOrderNo(sOrder.getOrderNo());
sOrder.setPayStatus(YesNoEnum.yes.getIndex());
}else {
response = orderPayService.payment(sOrder);
// 加入自动未支付自动取消队列 TODO 自动取消定时任务待开发
redisUtil.lPush(Constants.ORDER_AUTO_CANCEL_KEY, sOrder.getOrderNo());
}
save(sOrder);
return response;
}
private void checkOrderDate(CreateOrderRequest request) {
LambdaQueryWrapper<SOrder> queryWrapper = new LambdaQueryWrapper<>();
//订单开始时间在 【预定开始时间 和 预定结束时间+30分保洁】区间内
//订单结束时间在 【预定开始时间-30分钟保洁 和 预定结束时间】区间内
queryWrapper.apply("IFNULL(start_date,pre_start_date) BETWEEN '"
+ DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,request.getPreStartDate())
+ "' AND '" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,DateUtils.addMinutes(request.getPreEndDate(),30)) + "' " +
"OR IFNULL(end_date,pre_end_date) BETWEEN '"
+ DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,DateUtils.addMinutes(request.getPreStartDate(),-30))
+ "' AND '" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,request.getPreEndDate()) + "' ");
switch (OrderTypeEnum.getEnumByCode(request.getOrderType())){
case RESERVER://预定
break;
case RENEW://续费
if(StringUtils.isBlank(request.getPreOrderNo())){
throw new BaseException("续房订单编号不能为空!");
}
//排除上笔订单
queryWrapper.ne(SOrder::getOrderNo,request.getPreOrderNo());
break;
default:
throw new BaseException("订单类型异常!");
}
List<SOrder> orderList = list(queryWrapper);
if(CollectionUtils.isNotEmpty(orderList)){
throw new BaseException("下单时间已被预定!");
}
return null;
}
private void checkOrderPrice(SOrder order, SConsumer user){
......
......@@ -392,7 +392,7 @@ public class WechatNewServiceImpl implements WechatNewService {
payInfo.setOutTradeNo(vo.getOut_trade_no());
payInfo.setFeeType(vo.getFee_type());
payInfo.setTotalFee(vo.getTotal_fee());
payInfo.setSpbillCreateIp(vo.getSpbill_create_ip());
// payInfo.setSpbillCreateIp(vo.getSpbill_create_ip());
payInfo.setTimeStart(vo.getTime_start());
payInfo.setTimeExpire(vo.getTime_expire());
payInfo.setNotifyUrl(vo.getNotify_url());
......
package share.system.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import share.common.constant.Constants;
import share.common.constant.PayConstants;
import share.common.exception.base.BaseException;
import share.common.utils.BaseUtil;
import share.common.utils.XmlUtil;
import share.common.utils.wx.WXPayXmlUtil;
import share.system.domain.vo.CreateOrderRequestVo;
import share.system.domain.vo.WxRefundVo;
import javax.xml.parsers.DocumentBuilder;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* 微信支付工具类
*/
public class WxPayUtil {
/**
* 处理 HTTPS API返回数据,转换成Map对象。return_code为SUCCESS时,验证签名。
*
* @param xmlStr API返回的XML格式数据
* @return Map类型数据
* @throws Exception
*/
public static HashMap<String, Object> processResponseXml(String xmlStr) throws Exception {
String RETURN_CODE = "return_code";
String return_code;
HashMap<String, Object> respData = XmlUtil.xmlToMap(xmlStr);
if (respData.containsKey(RETURN_CODE)) {
return_code = (String) respData.get(RETURN_CODE);
} else {
throw new BaseException(String.format("No `return_code` in XML: %s", xmlStr));
}
if (return_code.equals(Constants.FAIL)) {
return respData;
} else if (return_code.equals(Constants.SUCCESS)) {
return respData;
} else {
throw new BaseException(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
}
}
/**
* 获取随机字符串,长度要求在32位以内。
*/
public static String getNonceStr() {
return DigestUtils.md5Hex(BaseUtil.getUuid() + BaseUtil.randomCount(111111, 666666));
}
/**
* 获取sign
* @param vo 微信公共下单对象
* @param signKey 微信签名key
* @return String
*/
public static String getSign(CreateOrderRequestVo vo, String signKey) {
// 对象转map
Map<String, Object> map = JSONObject.parseObject(JSONObject.toJSONString(vo), Map.class);
// map排序
Set<String> keySet = map.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(PayConstants.FIELD_SIGN)) {
continue;
}
if (ObjectUtil.isNotNull(map.get(k))) // 参数值为空,则不参与签名
sb.append(k).append("=").append(map.get(k)).append("&");
}
sb.append("key=").append(signKey);
String sign = SecureUtil.md5(sb.toString()).toUpperCase();
System.out.println("sign ========== " + sign);
return sign;
}
/**
* 获取sign
* @param wxRefundVo 微信退款对象
* @param signKey 微信签名key
* @return String
*/
public static String getSign(WxRefundVo wxRefundVo, String signKey) {
// 对象转map
Map<String, Object> map = JSONObject.parseObject(JSONObject.toJSONString(wxRefundVo), Map.class);
// map排序
Set<String> keySet = map.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(PayConstants.FIELD_SIGN)) {
continue;
}
if (ObjectUtil.isNotNull(map.get(k))) // 参数值为空,则不参与签名
sb.append(k).append("=").append(map.get(k)).append("&");
}
sb.append("key=").append(signKey);
String sign = SecureUtil.md5(sb.toString()).toUpperCase();
System.out.println("sign ========== " + sign);
return sign;
}
/**
* 获取sign
* @param map 待签名数据
* @param signKey 微信签名key
* @return String
*/
public static String getSign(Map<String, String> map, String signKey) {
// map排序
Set<String> keySet = map.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(PayConstants.FIELD_SIGN)) {
continue;
}
if (StrUtil.isNotBlank(map.get(k)) && map.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(map.get(k).trim()).append("&");
}
sb.append("key=").append(signKey);
String sign = SecureUtil.md5(sb.toString()).toUpperCase();
System.out.println("sign ========== " + sign);
return sign;
}
/**
* 获取sign
* @param map 待签名数据
* @param signKey 微信签名key
* @return String
*/
public static String getSignObject(Map<String, Object> map, String signKey) {
// map排序
Set<String> keySet = map.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(PayConstants.FIELD_SIGN)) {
continue;
}
if (ObjectUtil.isNotNull(map.get(k))) // 参数值为空,则不参与签名
sb.append(k).append("=").append(map.get(k)).append("&");
}
sb.append("key=").append(signKey);
String sign = SecureUtil.md5(sb.toString()).toUpperCase();
System.out.println("sign ========== " + sign);
return sign;
}
/**
* 获取当前时间戳,单位秒
* @return Long
*/
public static Long getCurrentTimestamp() {
return System.currentTimeMillis()/1000;
}
/**
* 获取当前时间戳,单位毫秒
* @return Long
*/
public static Long getCurrentTimestampMs() {
return System.currentTimeMillis();
}
/**
* XML格式字符串转换为Map
*
* @param strXML XML字符串
* @return XML数据转换后的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
System.out.println(StrUtil.format("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML));
throw ex;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment