diff --git a/sky-common/src/main/java/com/sky/utils/WeChatPayUtil.java b/sky-common/src/main/java/com/sky/utils/WeChatPayUtil.java index 804d7ed..6113438 100644 --- a/sky-common/src/main/java/com/sky/utils/WeChatPayUtil.java +++ b/sky-common/src/main/java/com/sky/utils/WeChatPayUtil.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.math.BigDecimal; +import java.nio.file.Files; import java.security.PrivateKey; import java.security.Signature; import java.security.cert.X509Certificate; @@ -188,7 +189,7 @@ public class WeChatPayUtil { byte[] message = signMessage.getBytes(); Signature signature = Signature.getInstance("SHA256withRSA"); - signature.initSign(PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath())))); + signature.initSign(PemUtil.loadPrivateKey(Files.newInputStream(new File(weChatProperties.getPrivateKeyFilePath()).toPath()))); signature.update(message); String packageSign = Base64.getEncoder().encodeToString(signature.sign()); diff --git a/sky-server/src/main/java/com/sky/controller/notify/PayNotifyController.java b/sky-server/src/main/java/com/sky/controller/notify/PayNotifyController.java new file mode 100644 index 0000000..df59cf4 --- /dev/null +++ b/sky-server/src/main/java/com/sky/controller/notify/PayNotifyController.java @@ -0,0 +1,117 @@ +package com.sky.controller.notify; + +import com.alibaba.druid.support.json.JSONUtils; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.sky.properties.WeChatProperties; +import com.sky.service.OrderService; +import com.wechat.pay.contrib.apache.httpclient.util.AesUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.entity.ContentType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; + +/** + * 支付回调相关接口 + */ +@RestController +@RequestMapping("/notify") +@Slf4j +public class PayNotifyController { + @Autowired + private OrderService orderService; + @Autowired + private WeChatProperties weChatProperties; + + /** + * 支付成功回调 + * + * @param request + */ + @RequestMapping("/paySuccess") + public void paySuccessNotify(HttpServletRequest request, HttpServletResponse response) throws Exception { + //读取数据 + String body = readData(request); + log.info("支付成功回调:{}", body); + + //数据解密 + String plainText = decryptData(body); + log.info("解密后的文本:{}", plainText); + + JSONObject jsonObject = JSON.parseObject(plainText); + String outTradeNo = jsonObject.getString("out_trade_no");//商户平台订单号 + String transactionId = jsonObject.getString("transaction_id");//微信支付交易号 + + log.info("商户平台订单号:{}", outTradeNo); + log.info("微信支付交易号:{}", transactionId); + + //业务处理,修改订单状态、来单提醒 + orderService.paySuccess(outTradeNo); + + //给微信响应 + responseToWeixin(response); + } + + /** + * 读取数据 + * + * @param request + * @return + * @throws Exception + */ + private String readData(HttpServletRequest request) throws Exception { + BufferedReader reader = request.getReader(); + StringBuilder result = new StringBuilder(); + String line = null; + while ((line = reader.readLine()) != null) { + if (result.length() > 0) { + result.append("\n"); + } + result.append(line); + } + return result.toString(); + } + + /** + * 数据解密 + * + * @param body + * @return + * @throws Exception + */ + private String decryptData(String body) throws Exception { + JSONObject resultObject = JSON.parseObject(body); + JSONObject resource = resultObject.getJSONObject("resource"); + String ciphertext = resource.getString("ciphertext"); + String nonce = resource.getString("nonce"); + String associatedData = resource.getString("associated_data"); + + AesUtil aesUtil = new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8)); + //密文解密 + String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), + nonce.getBytes(StandardCharsets.UTF_8), + ciphertext); + + return plainText; + } + + /** + * 给微信响应 + * @param response + */ + private void responseToWeixin(HttpServletResponse response) throws Exception{ + response.setStatus(200); + HashMap map = new HashMap<>(); + map.put("code", "SUCCESS"); + map.put("message", "SUCCESS"); + response.setHeader("Content-type", ContentType.APPLICATION_JSON.toString()); + response.getOutputStream().write(JSONUtils.toJSONString(map).getBytes(StandardCharsets.UTF_8)); + response.flushBuffer(); + } +} diff --git a/sky-server/src/main/java/com/sky/controller/user/OrderController.java b/sky-server/src/main/java/com/sky/controller/user/OrderController.java index 540cab4..9286c5b 100644 --- a/sky-server/src/main/java/com/sky/controller/user/OrderController.java +++ b/sky-server/src/main/java/com/sky/controller/user/OrderController.java @@ -1,16 +1,16 @@ package com.sky.controller.user; +import com.sky.dto.OrdersPaymentDTO; import com.sky.dto.OrdersSubmitDTO; import com.sky.result.Result; import com.sky.service.OrderService; +import com.sky.vo.OrderPaymentVO; import com.sky.vo.OrderSubmitVO; import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController("userOrderController") @RequestMapping("/user/order") @@ -22,9 +22,24 @@ public class OrderController { private OrderService orderService; @PostMapping("/submit") - public Result submit(@RequestBody OrdersSubmitDTO ordersSubmitDTO) { + public Result submit(@RequestBody OrdersSubmitDTO ordersSubmitDTO) { log.info("order submit: {}", ordersSubmitDTO); OrderSubmitVO orderSubmitVO = orderService.submitOrder(ordersSubmitDTO); return Result.success(orderSubmitVO); } + + /** + * 订单支付 + * + * @param ordersPaymentDTO + * @return + */ + @PutMapping("/payment") + @ApiOperation("订单支付") + public Result payment(@RequestBody OrdersPaymentDTO ordersPaymentDTO) throws Exception { + log.info("订单支付:{}", ordersPaymentDTO); + OrderPaymentVO orderPaymentVO = orderService.payment(ordersPaymentDTO); + log.info("生成预支付交易单:{}", orderPaymentVO); + return Result.success(orderPaymentVO); + } } diff --git a/sky-server/src/main/java/com/sky/mapper/OrderMapper.java b/sky-server/src/main/java/com/sky/mapper/OrderMapper.java index a8f3b3c..67628b1 100644 --- a/sky-server/src/main/java/com/sky/mapper/OrderMapper.java +++ b/sky-server/src/main/java/com/sky/mapper/OrderMapper.java @@ -2,9 +2,31 @@ package com.sky.mapper; import com.sky.entity.Orders; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +import java.time.LocalDateTime; @Mapper public interface OrderMapper { void insert(Orders orders); + + /** + * 根据订单号查询订单 + * + * @param orderNumber + */ + @Select("select * from orders where number = #{orderNumber}") + Orders getByNumber(String orderNumber); + + /** + * 修改订单信息 + * + * @param orders + */ + void update(Orders orders); + + @Update("update orders set status = #{orderStatus},pay_status = #{orderPaidStatus} ,checkout_time = #{check_out_time} where id = #{id}") + void updateStatus(Integer orderStatus, Integer orderPaidStatus, LocalDateTime check_out_time, Long id); } diff --git a/sky-server/src/main/java/com/sky/mapper/UserMapper.java b/sky-server/src/main/java/com/sky/mapper/UserMapper.java index 39cb86c..3a9c2d1 100644 --- a/sky-server/src/main/java/com/sky/mapper/UserMapper.java +++ b/sky-server/src/main/java/com/sky/mapper/UserMapper.java @@ -20,4 +20,7 @@ public interface UserMapper { * @param user */ void insert(User user); + + @Select("select * from sky_take_out.user where id = #{userId}") + User getById(Long userId); } diff --git a/sky-server/src/main/java/com/sky/service/OrderService.java b/sky-server/src/main/java/com/sky/service/OrderService.java index d9bf54b..84cc043 100644 --- a/sky-server/src/main/java/com/sky/service/OrderService.java +++ b/sky-server/src/main/java/com/sky/service/OrderService.java @@ -1,6 +1,8 @@ package com.sky.service; +import com.sky.dto.OrdersPaymentDTO; import com.sky.dto.OrdersSubmitDTO; +import com.sky.vo.OrderPaymentVO; import com.sky.vo.OrderSubmitVO; import org.springframework.stereotype.Service; @@ -13,4 +15,18 @@ public interface OrderService { * @return */ OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO); + + /** + * 订单支付 + * @param ordersPaymentDTO + * @return + */ + OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception; + + /** + * 支付成功,修改订单状态 + * @param outTradeNo + */ + void paySuccess(String outTradeNo); + } diff --git a/sky-server/src/main/java/com/sky/service/impl/OrderServiceImpl.java b/sky-server/src/main/java/com/sky/service/impl/OrderServiceImpl.java index 06760b1..93498f0 100644 --- a/sky-server/src/main/java/com/sky/service/impl/OrderServiceImpl.java +++ b/sky-server/src/main/java/com/sky/service/impl/OrderServiceImpl.java @@ -1,19 +1,18 @@ package com.sky.service.impl; +import com.alibaba.fastjson.JSONObject; import com.sky.constant.MessageConstant; import com.sky.context.BaseContext; +import com.sky.dto.OrdersPaymentDTO; import com.sky.dto.OrdersSubmitDTO; -import com.sky.entity.AddressBook; -import com.sky.entity.OrderDetail; -import com.sky.entity.Orders; -import com.sky.entity.ShoppingCart; +import com.sky.entity.*; import com.sky.exception.AddressBookBusinessException; +import com.sky.exception.OrderBusinessException; import com.sky.exception.ShoppingCartBusinessException; -import com.sky.mapper.AddressBookMapper; -import com.sky.mapper.OrderDetailMapper; -import com.sky.mapper.OrderMapper; -import com.sky.mapper.ShoppingCartMapper; +import com.sky.mapper.*; import com.sky.service.OrderService; +import com.sky.utils.WeChatPayUtil; +import com.sky.vo.OrderPaymentVO; import com.sky.vo.OrderSubmitVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; @@ -21,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -37,6 +37,10 @@ public class OrderServiceImpl implements OrderService { private ShoppingCartMapper shoppingCartMapper; @Autowired private OrderDetailMapper orderDetailMapper; + @Autowired + private UserMapper userMapper; + @Autowired + private WeChatPayUtil weChatPayUtil; @Override @Transactional @@ -90,4 +94,74 @@ public class OrderServiceImpl implements OrderService { .orderAmount(orders.getAmount()) .build(); } + + /** + * 订单支付 + * + * @param ordersPaymentDTO + * @return + */ + public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception { + // 当前登录用户id + + /*Long userId = BaseContext.getCurrentId(); + User user = userMapper.getById(userId); + + //调用微信支付接口,生成预支付交易单 + JSONObject jsonObject = weChatPayUtil.pay( + ordersPaymentDTO.getOrderNumber(), //商户订单号 + new BigDecimal(0.01), //支付金额,单位 元 + "苍穹外卖订单", //商品描述 + user.getOpenid() //微信用户的openid + ); + + if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) { + throw new OrderBusinessException("该订单已支付"); + }*/ + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("code","ORDERPAID"); + + OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class); + vo.setPackageStr(jsonObject.getString("package")); + + //为替代微信支付成功后的数据库订单状态更新,多定义一个方法进行修改 + + Integer OrderPaidStatus = Orders.PAID; //支付状态,已支付 + + Integer OrderStatus = Orders.TO_BE_CONFIRMED; //订单状态,待接单 + + + //发现没有将支付时间 check_out属性赋值,所以在这里更新 + + LocalDateTime check_out_time = LocalDateTime.now(); + + + Long orderId = orderMapper.getByNumber(ordersPaymentDTO.getOrderNumber()).getId(); + orderMapper.updateStatus(OrderStatus, OrderPaidStatus, check_out_time, orderId); + + return new OrderPaymentVO(); + } + + /** + * 支付成功,修改订单状态 + * + * @param outTradeNo + */ + public void paySuccess(String outTradeNo) { + + // 根据订单号查询订单 + Orders ordersDB = orderMapper.getByNumber(outTradeNo); + + // 根据订单id更新订单的状态、支付方式、支付状态、结账时间 + Orders orders = Orders.builder() + .id(ordersDB.getId()) + .status(Orders.TO_BE_CONFIRMED) + .payStatus(Orders.PAID) + .checkoutTime(LocalDateTime.now()) + .build(); + + orderMapper.update(orders); + } + } diff --git a/sky-server/src/main/resources/application.properties b/sky-server/src/main/resources/application.properties index bddeb21..8257aef 100644 --- a/sky-server/src/main/resources/application.properties +++ b/sky-server/src/main/resources/application.properties @@ -35,4 +35,11 @@ spring.servlet.multipart.max-file-size=${spring.servlet.multipart.max-file-size} spring.servlet.multipart.max-request-size=${spring.servlet.multipart.max-request-size} sky.wechat.appid=${sky.wechat.appid} -sky.wechat.secret=${sky.wechat.secret} \ No newline at end of file +sky.wechat.secret=${sky.wechat.secret} +sky.wechat.mchid=${sky.wechat.mchid} +sky.wechat.mch-serial-no=${sky.wechat.mch-serial-no} +sky.wechat.private-key-file-path=${sky.wechat.private-key-file-path} +sky.wechat.api-v3-key=${sky.wechat.api-v3-key} +sky.wechat.we-chat-pay-cert-file-path=${sky.wechat.we-chat-pay-cert-file-path} +sky.wechat.notify-url=${sky.wechat.notify-url} +sky.wechat.refund-notify-url=${sky.wechat.refund-notify-url} \ No newline at end of file diff --git a/sky-server/src/main/resources/mapper/OrderMapper.xml b/sky-server/src/main/resources/mapper/OrderMapper.xml index 2a80403..7e43d99 100644 --- a/sky-server/src/main/resources/mapper/OrderMapper.xml +++ b/sky-server/src/main/resources/mapper/OrderMapper.xml @@ -14,4 +14,34 @@ #{rejectionReason}, #{cancelTime}, #{estimatedDeliveryTime}, #{deliveryStatus}, #{deliveryTime}, #{packAmount}, #{tablewareNumber}, #{tablewareStatus}) + + update orders + + + cancel_reason=#{cancelReason}, + + + rejection_reason=#{rejectionReason}, + + + cancel_time=#{cancelTime}, + + + pay_status=#{payStatus}, + + + pay_method=#{payMethod}, + + + checkout_time=#{checkoutTime}, + + + status = #{status}, + + + delivery_time = #{deliveryTime} + + + where id = #{id} + \ No newline at end of file