| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- package com.cyksj.service.special.impl;
- import cn.hutool.core.date.DateUtil;
- import cn.hutool.core.util.StrUtil;
- import com.alipay.api.internal.util.AlipaySignature;
- import com.alipay.api.AlipayClient;
- import com.alipay.api.domain.AlipayTradeRefundModel;
- import com.alipay.api.request.AlipayTradeRefundRequest;
- import com.alipay.api.response.AlipayTradeRefundResponse;
- import com.baomidou.mybatisplus.core.toolkit.Wrappers;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.cyksj.common.EnvCommonService;
- import com.cyksj.common.exception.BusinessRuntimeException;
- import com.cyksj.common.snowflake.Sequence;
- import com.cyksj.common.util.Codec;
- import com.cyksj.common.util.Jsons;
- import com.cyksj.config.zfb.BaseZfbConfig;
- import com.cyksj.config.zfb.AliPayClientFactory;
- import com.cyksj.config.zfb.ZfbProductCode;
- import com.cyksj.mapper.channel.ShopConfigMapper;
- import com.cyksj.mapper.special.SpecialMemberEntitlementMapper;
- import com.cyksj.mapper.special.SpecialPayOrderMapper;
- import com.cyksj.mapper.special.SpecialPayProductMapper;
- import com.cyksj.model.dto.AliPayParams;
- import com.cyksj.model.entity.ShopConfig;
- import com.cyksj.model.entity.SpecialPayOrder;
- import com.cyksj.model.entity.SpecialPayProduct;
- import com.cyksj.model.entity.SpecialMemberEntitlement;
- import com.cyksj.model.request.SpecialPayCreateReq;
- import com.cyksj.model.request.OrderAttach;
- import com.cyksj.model.response.AliPayTradeStatus;
- import com.cyksj.model.response.SpecialPayCreateRep;
- import com.cyksj.model.response.SpecialMemberInfoRep;
- import com.cyksj.service.pay.AliPayCommonService;
- import com.cyksj.service.special.SpecialPayService;
- import lombok.RequiredArgsConstructor;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import java.math.BigDecimal;
- import java.math.RoundingMode;
- import java.util.Date;
- import java.util.List;
- import java.util.Map;
- import java.util.Optional;
- @Slf4j
- @Service
- @RequiredArgsConstructor
- public class SpecialPayServiceImpl implements SpecialPayService {
- private static final Sequence SEQUENCE = new Sequence(0);
- private static final int ORDER_EXPIRE_MINUTES = 5;
- private static final String YING_WANG_FEI_ALIPAY_APP_ID = "2021006115648946";
- private final SpecialPayProductMapper productMapper;
- private final SpecialPayOrderMapper orderMapper;
- private final SpecialMemberEntitlementMapper entitlementMapper;
- private final ShopConfigMapper shopConfigMapper;
- private final AliPayCommonService aliPayCommonService;
- private final EnvCommonService envCommonService;
- @Override
- @Transactional(rollbackFor = Throwable.class)
- public void refundOrder(String orderNo) throws Exception {
- SpecialPayOrder order = orderMapper.selectOne(Wrappers.lambdaQuery(SpecialPayOrder.class)
- .eq(SpecialPayOrder::getOrderNo, orderNo).last("limit 1 for update"));
- if (order == null) throw BusinessRuntimeException.getInstance("订单不存在");
- if (order.getStatus() == SpecialPayOrder.Status.REFUNDED) return;
- if (order.getStatus() != SpecialPayOrder.Status.PAID || StrUtil.isBlank(order.getAlipayTradeNo())) throw BusinessRuntimeException.getInstance("当前订单状态不支持退款");
- SpecialMemberEntitlement entitlement = entitlementMapper.selectOne(Wrappers.lambdaQuery(SpecialMemberEntitlement.class)
- .eq(SpecialMemberEntitlement::getUserId, order.getUserId()).last("limit 1 for update"));
- if (entitlement != null && !order.getId().equals(entitlement.getLastOrderId())) throw BusinessRuntimeException.getInstance("存在后续会员订单,暂不支持自动退款");
- AlipayTradeRefundModel model = new AlipayTradeRefundModel();
- model.setTradeNo(order.getAlipayTradeNo());
- model.setRefundAmount(order.getAmount().divide(BigDecimal.valueOf(100)).setScale(2, RoundingMode.UNNECESSARY).toPlainString());
- model.setRefundReason("特殊会员订单退款");
- model.setOutRequestNo("SR" + order.getOrderNo());
- AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
- refundRequest.setBizModel(model);
- AlipayTradeRefundResponse response = AliPayClientFactory.getAlipayClient(order.getShopId()).execute(refundRequest);
- if (response == null || !response.isSuccess()) throw BusinessRuntimeException.getInstance("支付宝退款失败");
- if (orderMapper.markRefunded(order.getId()) != 1) throw BusinessRuntimeException.getInstance("退款状态更新失败");
- if (entitlement != null) entitlementMapper.rollbackIfLastOrder(order.getUserId(), order.getDurationDays(), order.getId());
- }
- @Override
- public Page<SpecialPayOrder> getManageOrderPage(Integer start, Integer limit, String orderNo,
- Long userId, String productCode, SpecialPayOrder.Status status) {
- int pageNo = start == null || start < 1 ? 1 : start;
- int pageSize = limit == null || limit < 1 ? 10 : Math.min(limit, 100);
- return orderMapper.selectPage(new Page<>(pageNo, pageSize), Wrappers.lambdaQuery(SpecialPayOrder.class)
- .like(StrUtil.isNotBlank(orderNo), SpecialPayOrder::getOrderNo, orderNo)
- .eq(userId != null, SpecialPayOrder::getUserId, userId)
- .eq(StrUtil.isNotBlank(productCode), SpecialPayOrder::getProductCode, productCode)
- .eq(status != null, SpecialPayOrder::getStatus, status)
- .orderByDesc(SpecialPayOrder::getId));
- }
- @Override
- public List<SpecialPayProduct> getEnabledProducts() {
- return productMapper.selectList(Wrappers.lambdaQuery(SpecialPayProduct.class)
- .eq(SpecialPayProduct::getEnabled, true)
- .orderByAsc(SpecialPayProduct::getSortBy, SpecialPayProduct::getId));
- }
- @Override
- @Transactional(rollbackFor = Throwable.class)
- public SpecialPayCreateRep createAlipayOrder(Long userId, SpecialPayCreateReq request) throws Exception {
- SpecialPayProduct product = productMapper.selectOne(Wrappers.lambdaQuery(SpecialPayProduct.class)
- .eq(SpecialPayProduct::getProductCode, request.getProductCode())
- .eq(SpecialPayProduct::getEnabled, true)
- .last("limit 1"));
- if (product == null) {
- throw BusinessRuntimeException.getInstance("支付套餐不存在或已下架");
- }
- if (product.getAmount() == null || product.getAmount().compareTo(BigDecimal.ZERO) <= 0
- || product.getDurationDays() == null || product.getDurationDays() <= 0) {
- throw BusinessRuntimeException.getInstance("支付套餐配置异常");
- }
- String alipayProductCode = getAlipayProductCode(request.getPayType());
- ShopConfig shopConfig = shopConfigMapper.getByAppId(YING_WANG_FEI_ALIPAY_APP_ID);
- if (shopConfig == null || !StrUtil.equals("zfb", shopConfig.getType())) {
- throw BusinessRuntimeException.getInstance("未配置影网飞支付宝商户");
- }
- Date now = new Date();
- SpecialPayOrder order = new SpecialPayOrder();
- order.setOrderNo("SP" + SEQUENCE.nextId());
- order.setUserId(userId);
- order.setProductCode(product.getProductCode());
- order.setSubject(product.getProductName());
- order.setAmount(product.getAmount());
- order.setDurationDays(product.getDurationDays());
- order.setStatus(SpecialPayOrder.Status.CREATED);
- order.setFulfillStatus(SpecialPayOrder.FulfillStatus.INIT);
- order.setShopId(YING_WANG_FEI_ALIPAY_APP_ID);
- order.setExpireTime(DateUtil.offsetMinute(now, ORDER_EXPIRE_MINUTES));
- order.setCreatedTime(now);
- order.setUpdateTime(now);
- orderMapper.insert(order);
- String notifyUrl = envCommonService.getNotifyHost() + BaseZfbConfig.specialPayNotifyUrl;
- AliPayParams payParams = aliPayCommonService.builderPayParams(
- order.getId(), order.getOrderNo(), order.getShopId(), order.getAmount(),
- order.getCreatedTime(), order.getSubject(), alipayProductCode,
- request.getPayMode(), notifyUrl, request.getReturnUrl());
- if (payParams == null || StrUtil.isBlank(payParams.getBody())) {
- throw BusinessRuntimeException.getInstance("支付宝下单失败");
- }
- SpecialPayCreateRep response = new SpecialPayCreateRep();
- response.setOrderNo(order.getOrderNo());
- response.setStatus(order.getStatus());
- response.setExpireTime(order.getExpireTime());
- response.setPayBody(payParams.getBody());
- return response;
- }
- @Override
- public SpecialPayOrder getUserOrder(Long userId, String orderNo) {
- SpecialPayOrder order = orderMapper.selectOne(Wrappers.lambdaQuery(SpecialPayOrder.class)
- .eq(SpecialPayOrder::getOrderNo, orderNo)
- .eq(SpecialPayOrder::getUserId, userId)
- .last("limit 1"));
- if (order == null) {
- throw BusinessRuntimeException.getInstance("订单不存在");
- }
- return order;
- }
- @Override
- public SpecialMemberEntitlement getMemberEntitlement(Long userId) {
- return entitlementMapper.selectOne(Wrappers.lambdaQuery(SpecialMemberEntitlement.class)
- .eq(SpecialMemberEntitlement::getUserId, userId)
- .last("limit 1"));
- }
- @Override
- public SpecialMemberInfoRep getMemberInfo(Long userId) {
- SpecialMemberEntitlement entitlement = getMemberEntitlement(userId);
- Date now = new Date();
- SpecialMemberInfoRep response = new SpecialMemberInfoRep();
- response.setUserId(userId);
- response.setMember(entitlement != null && entitlement.getExpireTime() != null
- && entitlement.getExpireTime().after(now));
- response.setExpireTime(entitlement == null ? null : entitlement.getExpireTime());
- response.setRemainingDays(Boolean.TRUE.equals(response.getMember())
- ? Math.max(1L, (entitlement.getExpireTime().getTime() - now.getTime() + 86400000L - 1) / 86400000L)
- : 0L);
- if (entitlement != null && entitlement.getLastOrderId() != null && entitlement.getLastOrderId() > 0) {
- SpecialPayOrder lastOrder = orderMapper.selectById(entitlement.getLastOrderId());
- if (lastOrder != null && userId.equals(lastOrder.getUserId())) {
- response.setLastOrderNo(lastOrder.getOrderNo());
- response.setProductCode(lastOrder.getProductCode());
- response.setProductName(lastOrder.getSubject());
- response.setLastOrderStatus(lastOrder.getStatus());
- }
- }
- return response;
- }
- @Override
- @Transactional(rollbackFor = Throwable.class)
- public void handleAlipayNotify(Map<String, String> params) throws Exception {
- String appId = params.get("app_id");
- ShopConfig shopConfig = StrUtil.isBlank(appId) ? null : shopConfigMapper.getByAppId(appId);
- if (shopConfig == null || !AlipaySignature.rsaCheckV1(params, shopConfig.getPublicKey(),
- BaseZfbConfig.charset, BaseZfbConfig.signType)) {
- throw BusinessRuntimeException.getInstance("支付宝回调验签失败");
- }
- String orderNo = params.get("out_trade_no");
- SpecialPayOrder order = orderMapper.selectByOrderNoForUpdate(orderNo);
- if (order == null) {
- throw BusinessRuntimeException.getInstance("特殊支付订单不存在");
- }
- if (!StrUtil.equals(order.getShopId(), appId)) {
- throw BusinessRuntimeException.getInstance("支付宝回调商户不匹配");
- }
- String tradeStatus = params.get("trade_status");
- if (!AliPayTradeStatus.TRADE_SUCCESS.getStatus().equals(tradeStatus)
- && !AliPayTradeStatus.TRADE_FINISHED.getStatus().equals(tradeStatus)) {
- throw BusinessRuntimeException.getInstance("支付宝交易状态未成功");
- }
- verifyOrderIdentity(params, order);
- verifyAmount(params.get("total_amount"), order.getAmount());
- String tradeNo = params.get("trade_no");
- if (StrUtil.isBlank(tradeNo)) {
- throw BusinessRuntimeException.getInstance("支付宝交易号为空");
- }
- if (order.getStatus() == SpecialPayOrder.Status.PAID) {
- if (!StrUtil.equals(order.getAlipayTradeNo(), tradeNo)) {
- throw BusinessRuntimeException.getInstance("支付宝交易号不匹配");
- }
- return;
- }
- if (order.getStatus() != SpecialPayOrder.Status.CREATED) {
- throw BusinessRuntimeException.getInstance("当前订单状态不可支付");
- }
- Date payTime = new Date();
- if (StrUtil.isNotBlank(params.get("gmt_payment"))) {
- payTime = DateUtil.parse(params.get("gmt_payment"), "yyyy-MM-dd HH:mm:ss");
- }
- String buyerId = Optional.ofNullable(params.get("buyer_id"))
- .orElse(params.get("buyer_open_id"));
- if (orderMapper.markPaid(order.getId(), tradeNo, buyerId, payTime) != 1) {
- throw BusinessRuntimeException.getInstance("订单支付状态更新失败");
- }
- if (order.getDurationDays() == null || order.getDurationDays() <= 0) {
- throw BusinessRuntimeException.getInstance("订单套餐配置异常");
- }
- entitlementMapper.extend(order.getUserId(), order.getDurationDays(), order.getId());
- if (orderMapper.markFulfilled(order.getId()) != 1) {
- throw BusinessRuntimeException.getInstance("会员权益发放失败");
- }
- log.info("特殊支付宝订单支付并履约成功, orderNo:{}, tradeNo:{}", orderNo, tradeNo);
- }
- private String getAlipayProductCode(Integer payType) {
- if (Integer.valueOf(1).equals(payType)) {
- return ZfbProductCode.MOBILE_WEB.getProductCode();
- }
- if (Integer.valueOf(2).equals(payType)) {
- return ZfbProductCode.PC_WEB.getProductCode();
- }
- throw BusinessRuntimeException.getInstance("不支持的支付宝支付方式");
- }
- private void verifyOrderIdentity(Map<String, String> params, SpecialPayOrder order) {
- try {
- String body = Codec.DoBase64.custom().setData(params.get("body")).decodeAsText();
- OrderAttach attach = Jsons.parseObject(body, OrderAttach.class);
- if (attach == null || !order.getId().equals(attach.getO())
- || !StrUtil.equals(order.getOrderNo(), params.get("out_trade_no"))) {
- throw BusinessRuntimeException.getInstance("支付宝回调订单不匹配");
- }
- } catch (BusinessRuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw BusinessRuntimeException.getInstance("支付宝回调订单参数异常");
- }
- }
- private void verifyAmount(String totalAmount, BigDecimal expectedCents) {
- try {
- BigDecimal callbackCents = new BigDecimal(totalAmount)
- .multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.UNNECESSARY);
- if (expectedCents == null || callbackCents.compareTo(expectedCents) != 0) {
- throw BusinessRuntimeException.getInstance("支付宝回调金额不匹配");
- }
- } catch (BusinessRuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw BusinessRuntimeException.getInstance("支付宝回调金额异常");
- }
- }
- }
|