package com.izouma.nineth.service; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alipay.api.AlipayClient; import com.alipay.api.request.AlipayTradeWapPayRequest; import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.constant.WxPayConstants; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.WxPayService; import com.huifu.adapay.core.exception.BaseAdaPayException; import com.huifu.adapay.model.AdapayCommon; import com.huifu.adapay.model.Payment; import com.izouma.nineth.config.AdapayProperties; import com.izouma.nineth.config.AlipayProperties; import com.izouma.nineth.config.GeneralProperties; import com.izouma.nineth.config.WxPayProperties; import com.izouma.nineth.domain.Asset; import com.izouma.nineth.domain.GiftOrder; import com.izouma.nineth.domain.User; import com.izouma.nineth.enums.AssetStatus; import com.izouma.nineth.enums.OrderStatus; import com.izouma.nineth.enums.PayMethod; import com.izouma.nineth.exception.BusinessException; import com.izouma.nineth.repo.*; import com.izouma.nineth.utils.SnowflakeIdWorker; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.EncoderException; import org.apache.commons.codec.net.URLCodec; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.core.env.Environment; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.ui.Model; import javax.transaction.Transactional; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @Service @AllArgsConstructor @Slf4j public class GiftOrderService { private AssetRepo assetRepo; private UserRepo userRepo; private SysConfigService sysConfigService; private GiftOrderRepo giftOrderRepo; private AlipayProperties alipayProperties; private AlipayClient alipayClient; private WxPayProperties wxPayProperties; private WxPayService wxPayService; private Environment env; private AssetService assetService; private AdapayProperties adapayProperties; private GeneralProperties generalProperties; private SnowflakeIdWorker snowflakeIdWorker; private ErrorOrderRepo errorOrderRepo; @Transactional public GiftOrder gift(Long userId, Long assetId, Long toUserId) { Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("资产不存在")); if (!asset.getUserId().equals(userId)) { throw new BusinessException("无权限"); } int holdDays; if (ObjectUtils.isEmpty(asset.getHoldDays())) { holdDays = sysConfigService.getInt("hold_days"); } else { holdDays = asset.getHoldDays(); } if (ChronoUnit.DAYS.between(asset.getCreatedAt(), LocalDateTime.now()) < holdDays) { throw new BusinessException("需持有满" + holdDays + "天才能转赠"); } if (toUserId.equals(userId)) { throw new BusinessException("不能送给自己"); } if (!(asset.getStatus() == AssetStatus.NORMAL)) { throw new BusinessException("当前状态不可转赠"); } if (asset.isConsignment()) { throw new BusinessException("请先取消寄售"); } if (asset.isPublicShow()) { assetService.cancelPublic(asset); } asset.setStatus(AssetStatus.GIFTING); assetRepo.save(asset); GiftOrder giftOrder = GiftOrder.builder() .userId(userId) .assetId(assetId) .toUserId(toUserId) .gasPrice(sysConfigService.getBigDecimal("gas_fee")) .status(OrderStatus.NOT_PAID) .build(); return giftOrderRepo.save(giftOrder); } @Transactional public void giftNotify(Long orderId, PayMethod payMethod, String transactionId) { GiftOrder giftOrder = giftOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在")); log.error("转赠回调 orderId={} transactionId={}", orderId, transactionId); if (giftOrder.getStatus() == OrderStatus.NOT_PAID) { Asset asset = assetRepo.findById(giftOrder.getAssetId()).orElseThrow(new BusinessException("资产不存在")); User newOwner = userRepo.findById(giftOrder.getToUserId()).orElseThrow(new BusinessException("用户不存在")); giftOrder.setPayMethod(payMethod); giftOrder.setStatus(OrderStatus.FINISH); giftOrder.setTransactionId(transactionId); giftOrder.setPayTime(LocalDateTime.now()); giftOrder.setPayMethod(PayMethod.ALIPAY); assetService.transfer(asset, asset.getPrice(), newOwner, "转赠", null); } else { log.error("转赠回调出错 状态错误 orderid={} transactionid={} status={}", orderId, transactionId, giftOrder .getStatus()); errorOrderRepo.save(ErrorOrder.builder() .orderId(orderId) .transactionId(transactionId) .payMethod(payMethod) .type("gift") .build()); } } public void payOrderAlipay(Long id, Model model) { try { GiftOrder order = giftOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在")); if (order.getStatus() != OrderStatus.NOT_PAID) { throw new BusinessException("订单状态错误"); } JSONObject bizContent = new JSONObject(); bizContent.put("notifyUrl", alipayProperties.getNotifyUrl()); bizContent.put("returnUrl", alipayProperties.getReturnUrl()); bizContent.put("out_trade_no", String.valueOf(snowflakeIdWorker.nextId())); bizContent.put("total_amount", order.getGasPrice().stripTrailingZeros().toPlainString()); bizContent.put("disable_pay_channels", "pcredit,creditCard"); if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) { // 测试环境设为1分 bizContent.put("total_amount", "0.01"); } bizContent.put("subject", "转赠GAS费"); bizContent.put("product_code", "QUICK_WAP_PAY"); JSONObject body = new JSONObject(); body.put("action", "payGiftOrder"); body.put("userId", order.getUserId()); body.put("orderId", order.getId()); bizContent.put("body", body.toJSONString()); AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest(); alipayRequest.setReturnUrl(alipayProperties.getReturnUrl()); alipayRequest.setNotifyUrl(alipayProperties.getNotifyUrl()); alipayRequest.setBizContent(JSON.toJSONString(bizContent)); String form = alipayClient.pageExecute(alipayRequest).getBody(); model.addAttribute("form", form); } catch (BusinessException err) { model.addAttribute("errMsg", err.getError()); } catch (Exception e) { model.addAttribute("errMsg", e.getMessage()); } } public Object payOrderWeixin(Long id, String tradeType, String openId) throws WxPayException, EncoderException { GiftOrder order = giftOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在")); if (order.getStatus() != OrderStatus.NOT_PAID) { throw new BusinessException("订单状态错误"); } WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest(); request.setBody("转赠GAS费"); request.setOutTradeNo(String.valueOf(new SnowflakeIdWorker(1, 1).nextId())); request.setTotalFee(order.getGasPrice().multiply(BigDecimal.valueOf(100)).intValue()); if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) { // 测试环境设为1分 // request.setTotalFee(1); } request.setSpbillCreateIp("180.102.110.170"); request.setNotifyUrl(wxPayProperties.getNotifyUrl()); request.setTradeType(tradeType); request.setOpenid(openId); request.setSignType("MD5"); JSONObject body = new JSONObject(); body.put("action", "payGiftOrder"); body.put("userId", order.getUserId()); body.put("orderId", order.getId()); request.setAttach(body.toJSONString()); if (WxPayConstants.TradeType.MWEB.equals(tradeType)) { WxPayMwebOrderResult result = wxPayService.createOrder(request); return result.getMwebUrl() + "&redirect_url=" + new URLCodec().encode(wxPayProperties.getReturnUrl()); } else if (WxPayConstants.TradeType.JSAPI.equals(tradeType)) { return wxPayService.createOrder(request); } throw new BusinessException("不支持此付款方式"); } public Object payAdapay(Long id, String payChannel, String openId) throws BaseAdaPayException { List aliChannels = Arrays.asList("alipay", "alipay_qr", "alipay_wap"); List wxChannels = Arrays.asList("wx_pub", "wx_lite"); if (!aliChannels.contains(payChannel) && !wxChannels.contains(payChannel)) { throw new BusinessException("不支持此渠道"); } GiftOrder order = giftOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在")); if (order.getGasPrice().compareTo(BigDecimal.ZERO) == 0) { giftNotify(id, PayMethod.ALIPAY, id.toString()); return order.getId().toString(); } User invitor = null; if (order.getStatus() != OrderStatus.NOT_PAID) { throw new BusinessException("订单状态错误"); } Map paymentParams = new HashMap<>(); paymentParams.put("order_no", String.valueOf(snowflakeIdWorker.nextId())); paymentParams.put("pay_amt", order.getGasPrice().setScale(2, RoundingMode.HALF_UP).toPlainString()); paymentParams.put("app_id", adapayProperties.getAppId()); paymentParams.put("pay_channel", payChannel); paymentParams.put("goods_title", "转赠GAS费"); paymentParams.put("goods_desc", "转赠GAS费"); paymentParams.put("time_expire", DateTimeFormatter.ofPattern("yyyyMMddHHmmss") .format(LocalDateTime.now().plusMinutes(5))); paymentParams.put("notify_url", adapayProperties.getNotifyUrl() + "/giftOrder/" + order.getId()); Map expend = new HashMap<>(); paymentParams.put("expend", expend); if ("wx_pub".equals(payChannel)) { if (StringUtils.isBlank(openId)) { throw new BusinessException("缺少openId"); } expend.put("open_id", openId); expend.put("limit_pay", "1"); } Map response; if ("wx_lite".equals(payChannel)) { paymentParams.put("adapay_func_code", "wxpay.createOrder"); paymentParams.put("callback_url", generalProperties.getHost() + "/9th/orders"); response = AdapayCommon.requestAdapayUits(paymentParams); log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat)); } else { response = Payment.create(paymentParams); log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat)); AdapayService.checkSuccess(response); } switch (payChannel) { case "alipay_wap": case "alipay": return MapUtils.getString(MapUtils.getMap(response, "expend"), "pay_info"); case "alipay_qr": return MapUtils.getString(MapUtils.getMap(response, "expend"), "qrcode_url"); case "wx_pub": JSONObject payParams = JSON .parseObject(MapUtils.getString(MapUtils.getMap(response, "expend"), "pay_info")); payParams.put("timestamp", payParams.get("timeStamp")); payParams.remove("timeStamp"); return payParams; default: return MapUtils.getMap(response, "expend"); } } @Scheduled(fixedRate = 60000) public void batchCancel() { if (generalProperties.isNotifyServer()) { return; } if (Arrays.asList(env.getActiveProfiles()).contains("dev")) { return; } List orders = giftOrderRepo.findByStatusAndCreatedAtBeforeAndDelFalse(OrderStatus.NOT_PAID, LocalDateTime.now().minusMinutes(5)); orders.forEach(o -> { try { cancel(o); } catch (Exception ignored) { } }); } public void cancel(GiftOrder order) { if (order.getStatus() != OrderStatus.NOT_PAID) { throw new BusinessException("已支付订单无法取消"); } Asset asset = assetRepo.findById(order.getAssetId()).orElseThrow(new BusinessException("藏品不存在")); log.info("set normal giftOrder {}", order.getId()); asset.setStatus(AssetStatus.NORMAL); assetRepo.save(asset); order.setStatus(OrderStatus.CANCELLED); order.setCancelTime(LocalDateTime.now()); giftOrderRepo.save(order); } }