diff --git a/ruoyi-mall/src/main/java/com/cyl/h5/controller/H5OrderController.java b/ruoyi-mall/src/main/java/com/cyl/h5/controller/H5OrderController.java index 213e0e4..9892cc2 100644 --- a/ruoyi-mall/src/main/java/com/cyl/h5/controller/H5OrderController.java +++ b/ruoyi-mall/src/main/java/com/cyl/h5/controller/H5OrderController.java @@ -4,6 +4,8 @@ import cn.hutool.core.collection.CollectionUtil; import com.alibaba.fastjson.JSONObject; import com.cyl.h5.pojo.dto.OrderCreateDTO; import com.cyl.h5.pojo.request.CancelOrderRequest; +import com.cyl.h5.pojo.request.OrderPayRequest; +import com.cyl.h5.pojo.response.OrderPayResponse; import com.cyl.h5.pojo.vo.CountOrderVO; import com.cyl.h5.pojo.vo.H5OrderVO; import com.cyl.h5.pojo.vo.OrderCalcVO; @@ -131,4 +133,28 @@ public class H5OrderController { } } } + + @ApiOperation("订单支付") + @PostMapping("/orderPay") + public ResponseEntity orderPay(@RequestBody OrderPayRequest req){ + log.info("订单支付","提交的数据:"+JSONObject.toJSONString(req)); + String redisKey = "h5_oms_order_pay_"+req.getPayId(); + String redisValue = req.getPayId()+"_"+System.currentTimeMillis(); + try { + redisService.lock(redisKey, redisValue, 60); + Member member = (Member) LocalDataUtil.getVar(Constants.MEMBER_INFO); + Long memberId = member.getId(); + req.setMemberId(memberId); + return ResponseEntity.ok(service.orderPay(req)); + }catch (Exception e){ + log.error("支付方法异常", e); + throw new RuntimeException("支付失败"); + }finally { + try{ + redisService.unLock(redisKey,redisValue); + }catch (Exception e){ + log.error("",e); + } + } + } } diff --git a/ruoyi-mall/src/main/java/com/cyl/h5/pojo/request/OrderPayRequest.java b/ruoyi-mall/src/main/java/com/cyl/h5/pojo/request/OrderPayRequest.java new file mode 100644 index 0000000..d097b7e --- /dev/null +++ b/ruoyi-mall/src/main/java/com/cyl/h5/pojo/request/OrderPayRequest.java @@ -0,0 +1,18 @@ +package com.cyl.h5.pojo.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("订单支付请求体") +public class OrderPayRequest { + @ApiModelProperty(value = "支付id", required = true) + private Long payId; + + @ApiModelProperty(value = "支付方式: 1-支付宝 2-微信(默认)", required = true) + private Integer type; + + @ApiModelProperty(value = "用户id", hidden = true) + private Long memberId; +} diff --git a/ruoyi-mall/src/main/java/com/cyl/h5/pojo/response/OrderPayResponse.java b/ruoyi-mall/src/main/java/com/cyl/h5/pojo/response/OrderPayResponse.java new file mode 100644 index 0000000..d61733e --- /dev/null +++ b/ruoyi-mall/src/main/java/com/cyl/h5/pojo/response/OrderPayResponse.java @@ -0,0 +1,33 @@ +package com.cyl.h5.pojo.response; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author 86199 + */ +@Data +@ApiModel("支付响应") +public class OrderPayResponse { + @ApiModelProperty(value = "支付方式:1-支付宝,2-微信(默认)", dataType = "Integer") + private Integer payType; + + @ApiModelProperty("appId") + private String appId; + + @ApiModelProperty("timeStamp") + private String timeStamp; + + @ApiModelProperty("signType") + private String signType; + + @ApiModelProperty("package") + private String package_; + + @ApiModelProperty("nonceStr") + private String nonceStr; + + @ApiModelProperty("签名") + private String paySign; +} diff --git a/ruoyi-mall/src/main/java/com/cyl/h5/service/H5MemberService.java b/ruoyi-mall/src/main/java/com/cyl/h5/service/H5MemberService.java index 191a03d..305eca7 100644 --- a/ruoyi-mall/src/main/java/com/cyl/h5/service/H5MemberService.java +++ b/ruoyi-mall/src/main/java/com/cyl/h5/service/H5MemberService.java @@ -212,6 +212,10 @@ public class H5MemberService { MemberVO memberVO = new MemberVO(); BeanUtils.copyProperties(member, memberVO); memberVO.setPhone(AesCryptoUtils.decrypt(aesKey, member.getPhoneEncrypted())); + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("member_id", member.getId()); + MemberWechat memberWechat = memberWechatMapper.selectOne(qw); + memberVO.setOpenId(memberWechat.getOpenid()); return memberVO; } } diff --git a/ruoyi-mall/src/main/java/com/cyl/h5/service/H5OrderService.java b/ruoyi-mall/src/main/java/com/cyl/h5/service/H5OrderService.java index 3b48865..f9839ca 100644 --- a/ruoyi-mall/src/main/java/com/cyl/h5/service/H5OrderService.java +++ b/ruoyi-mall/src/main/java/com/cyl/h5/service/H5OrderService.java @@ -1,6 +1,7 @@ package com.cyl.h5.service; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; @@ -8,6 +9,8 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.cyl.h5.pojo.dto.OrderCreateDTO; import com.cyl.h5.pojo.dto.OrderProductListDTO; import com.cyl.h5.pojo.request.CancelOrderRequest; +import com.cyl.h5.pojo.request.OrderPayRequest; +import com.cyl.h5.pojo.response.OrderPayResponse; import com.cyl.h5.pojo.vo.CountOrderVO; import com.cyl.h5.pojo.vo.H5OrderVO; import com.cyl.h5.pojo.vo.OrderCalcVO; @@ -28,24 +31,37 @@ import com.cyl.manager.pms.mapper.SkuMapper; import com.cyl.manager.ums.domain.Member; import com.cyl.manager.ums.domain.MemberAddress; import com.cyl.manager.ums.domain.MemberCart; +import com.cyl.manager.ums.domain.MemberWechat; import com.cyl.manager.ums.mapper.MemberAddressMapper; import com.cyl.manager.ums.mapper.MemberCartMapper; +import com.cyl.manager.ums.mapper.MemberWechatMapper; +import com.cyl.wechat.WechatPayData; +import com.cyl.wechat.WechatPayService; +import com.cyl.wechat.WechatPayUtil; import com.github.pagehelper.PageHelper; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.utils.IDGenerator; import com.ruoyi.framework.config.LocalDataUtil; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.IOException; import java.math.BigDecimal; +import java.net.URISyntaxException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; @Service public class H5OrderService { @@ -77,6 +93,12 @@ public class H5OrderService { @Autowired private OrderOperateHistoryService orderOperateHistoryService; + @Autowired + private WechatPayService wechatPayService; + + @Autowired + private MemberWechatMapper memberWechatMapper; + @Transactional public Long submit(OrderSubmitForm form) { Member member = (Member) LocalDataUtil.getVar(Constants.MEMBER_INFO); @@ -124,8 +146,11 @@ public class H5OrderService { //生成一个统一的订单号 Long orderId = IDGenerator.generateId(); + //生产一个payId + Long payId = IDGenerator.generateId(); //创建订单 Order order = new Order(); + order.setPayId(payId); order.setId(orderId); order.setOrderSn(this.getOrderIdPrefix() + orderId); order.setMemberId(member.getId()); @@ -135,8 +160,7 @@ public class H5OrderService { order.setPurchasePrice(BigDecimal.ZERO); order.setFreightAmount(BigDecimal.ZERO); order.setPayAmount(orderTotalAmount); - //暂时为接入支付,直接设置为待发货 - order.setStatus(Constants.OrderStatus.SEND); + order.setStatus(Constants.OrderStatus.NOTPAID); order.setAftersaleStatus(1); order.setReceiverName(memberAddress.getName()); order.setReceiverPhone(memberAddress.getPhoneHidden()); @@ -151,7 +175,7 @@ public class H5OrderService { order.setNote(form.getNote()); order.setConfirmStatus(0); order.setDeleteStatus(0); - order.setPaymentTime(optTime); +// order.setPaymentTime(optTime); order.setCreateTime(optTime); order.setCreateBy(member.getId()); int rows = orderMapper.insert(order); @@ -164,7 +188,7 @@ public class H5OrderService { OrderOperateHistory orderOperateHistory = new OrderOperateHistory(); orderOperateHistory.setOrderId(orderId); orderOperateHistory.setOperateMan(member.getId() + ""); - orderOperateHistory.setOrderStatus(Constants.OrderStatus.SEND); + orderOperateHistory.setOrderStatus(Constants.OrderStatus.NOTPAID); orderOperateHistory.setCreateTime(optTime); orderOperateHistory.setCreateBy(member.getId()); rows = orderOperateHistoryMapper.insert(orderOperateHistory); @@ -183,7 +207,7 @@ public class H5OrderService { } } //当前订单id,接入支付后可返回payId - return orderId; + return payId; } public OrderCalcVO addOrderCheck(OrderCreateDTO orderCreateDTO) { @@ -398,4 +422,59 @@ public class H5OrderService { } return "取消订单成功"; } + + /** + * 订单支付 + * @param req 支付请求 + * @return + */ + public OrderPayResponse orderPay(OrderPayRequest req) { + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("pay_id", req.getPayId()); + qw.eq("status", 0); + List orderList = orderMapper.selectList(qw); + if (CollectionUtil.isEmpty(orderList)){ + throw new RuntimeException("没有待支付的订单"); + } + QueryWrapper memberWechatQw = new QueryWrapper<>(); + memberWechatQw.eq("member_id", req.getMemberId()); + MemberWechat memberWechat = memberWechatMapper.selectOne(memberWechatQw); + if (memberWechat == null || StrUtil.isBlank(memberWechat.getOpenid())){ + throw new RuntimeException("获取用户openId失败"); + } + QueryWrapper orderItemQw = new QueryWrapper<>(); + orderItemQw.eq("order_id", orderList.get(0).getId()); + List orderItemList = orderItemMapper.selectList(orderItemQw); + String orderDesc = orderItemList.get(0).getProductName().substring(0, Math.min(40, orderItemList.get(0).getProductName().length())); + String prepayId = wechatPayService.jsapiPay( + String.valueOf(req.getPayId()), + orderDesc, + Integer.valueOf(orderList.stream().map(Order::getPayAmount). + reduce(BigDecimal.ZERO, BigDecimal::add).multiply(new BigDecimal(100)).stripTrailingZeros().toPlainString()), + memberWechat.getOpenid() + ); + OrderPayResponse response = new OrderPayResponse(); + response.setPayType(2); + String appId = WechatPayData.appId; + String nonceStr = WechatPayUtil.generateNonceStr(); + long timeStamp = WechatPayUtil.getCurrentTimestamp(); + prepayId = "prepay_id=" + prepayId; + String signType = "RSA"; + String paySign = null; + String signatureStr = Stream.of(appId, String.valueOf(timeStamp), nonceStr, prepayId) + .collect(Collectors.joining("\n", "", "\n")); + try { + paySign = WechatPayUtil.getSign(signatureStr, WechatPayData.privateKeyPath); + } catch (Exception e) { + throw new RuntimeException("支付失败"); + } + response.setAppId(appId); + response.setTimeStamp(String.valueOf(timeStamp)); + response.setNonceStr(nonceStr); + response.setSignType(signType); + response.setPackage_(prepayId); + response.setPaySign(paySign); + return response; + } + } diff --git a/ruoyi-mall/src/main/java/com/cyl/manager/oms/domain/Order.java b/ruoyi-mall/src/main/java/com/cyl/manager/oms/domain/Order.java index c21088d..97710e0 100644 --- a/ruoyi-mall/src/main/java/com/cyl/manager/oms/domain/Order.java +++ b/ruoyi-mall/src/main/java/com/cyl/manager/oms/domain/Order.java @@ -26,6 +26,9 @@ public class Order extends BaseAudit { @TableId(type = IdType.ASSIGN_ID) private Long id; + @ApiModelProperty("支付id") + private Long payId; + @ApiModelProperty("订单编号") @Excel(name = "订单编号") private String orderSn; diff --git a/ruoyi-mall/src/main/java/com/cyl/manager/oms/pojo/vo/OrderVO.java b/ruoyi-mall/src/main/java/com/cyl/manager/oms/pojo/vo/OrderVO.java index 203a5f1..cc67917 100644 --- a/ruoyi-mall/src/main/java/com/cyl/manager/oms/pojo/vo/OrderVO.java +++ b/ruoyi-mall/src/main/java/com/cyl/manager/oms/pojo/vo/OrderVO.java @@ -18,6 +18,8 @@ import lombok.Data; public class OrderVO extends BaseAudit { /** 订单id */ private Long id; + /** 支付id */ + private Long payId; /** 订单编号 */ private String orderSn; /** MEMBER_ID */ diff --git a/ruoyi-mall/src/main/java/com/cyl/manager/ums/pojo/vo/MemberVO.java b/ruoyi-mall/src/main/java/com/cyl/manager/ums/pojo/vo/MemberVO.java index 14e132b..65c8e95 100644 --- a/ruoyi-mall/src/main/java/com/cyl/manager/ums/pojo/vo/MemberVO.java +++ b/ruoyi-mall/src/main/java/com/cyl/manager/ums/pojo/vo/MemberVO.java @@ -64,4 +64,7 @@ public class MemberVO extends BaseAudit { /** 用户剩余积分 */ @Excel(name = "用户剩余积分") private BigDecimal integral; + /** openId */ + @Excel(name = "openId") + private String openId; } diff --git a/ruoyi-mall/src/main/java/com/cyl/wechat/WechatPayUtil.java b/ruoyi-mall/src/main/java/com/cyl/wechat/WechatPayUtil.java new file mode 100644 index 0000000..7468277 --- /dev/null +++ b/ruoyi-mall/src/main/java/com/cyl/wechat/WechatPayUtil.java @@ -0,0 +1,80 @@ +package com.cyl.wechat; + + + +import cn.hutool.crypto.PemUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.Base64Utils; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.*; +import java.util.*; + + +public class WechatPayUtil { + + private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + private static final Random RANDOM = new SecureRandom(); + + + public static String getSign(String signatureStr,String privateKey) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException, URISyntaxException { + //replace 根据实际情况,不一定都需要 + String replace = privateKey.replace("\\n", "\n"); + ClassPathResource classPathResource = new ClassPathResource(privateKey); + InputStream certStream = classPathResource.getInputStream(); + PrivateKey merchantPrivateKey = PemUtil.readPemPrivateKey(certStream); + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initSign(merchantPrivateKey); + sign.update(signatureStr.getBytes(StandardCharsets.UTF_8)); + return Base64Utils.encodeToString(sign.sign()); + } + + /** + * 获取随机字符串 Nonce Str + * + * @return String 随机字符串 + */ + public static String generateNonceStr() { + char[] nonceChars = new char[32]; + for (int index = 0; index < nonceChars.length; ++index) { + nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length())); + } + return new String(nonceChars); + } + + + /** + * 日志 + * @return + */ + public static Logger getLogger() { + Logger logger = LoggerFactory.getLogger("wxpay java sdk"); + return logger; + } + + /** + * 获取当前时间戳,单位秒 + * @return + */ + public static long getCurrentTimestamp() { + return System.currentTimeMillis()/1000; + } + + /** + * 获取当前时间戳,单位毫秒 + * @return + */ + public static long getCurrentTimestampMs() { + return System.currentTimeMillis(); + } + +} \ No newline at end of file