PayNotifyLogic.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | LikeShop100%开源免费商用电商系统
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | 开源版本可自由商用,可去除界面版权logo
  7. // | 商业版本务必购买商业授权,以免引起法律纠纷
  8. // | 禁止对系统程序代码以任何目的,任何形式的再发布
  9. // | Gitee下载:https://gitee.com/likeshop_gitee/likeshop
  10. // | 访问官网:https://www.likemarket.net
  11. // | 访问社区:https://home.likemarket.net
  12. // | 访问手册:http://doc.likemarket.net
  13. // | 微信公众号:好象科技
  14. // | 好象科技开发团队 版权所有 拥有最终解释权
  15. // +----------------------------------------------------------------------
  16. // | Author: LikeShopTeam
  17. // +----------------------------------------------------------------------
  18. namespace app\common\logic;
  19. use app\adminapi\logic\distribution\DistributionLevelLogic;
  20. use app\common\cache\HandleConcurrencyCache;
  21. use app\common\cache\YlyPrinterCache;
  22. use app\common\enum\AccountLogEnum;
  23. use app\common\enum\DeliveryEnum;
  24. use app\common\enum\FootprintEnum;
  25. use app\common\enum\IntegralGoodsEnum;
  26. use app\common\enum\IntegralOrderEnum;
  27. use app\common\enum\NoticeEnum;
  28. use app\common\enum\OrderEnum;
  29. use app\common\enum\OrderLogEnum;
  30. use app\common\enum\PayEnum;
  31. use app\common\enum\PrinterEnum;
  32. use app\common\model\IntegralGoods;
  33. use app\common\model\IntegralOrder;
  34. use app\common\model\Order;
  35. use app\common\model\OrderGoods;
  36. use app\common\model\OrderLog;
  37. use app\common\model\Presell;
  38. use app\common\model\PresellGoodsItem;
  39. use app\common\model\RechargeOrder;
  40. use app\common\model\SeckillGoodsItem;
  41. use app\common\model\SelffetchShop;
  42. use app\common\model\TeamFound;
  43. use app\common\model\TeamGoodsItem;
  44. use app\common\model\User;
  45. use app\common\service\ConfigService;
  46. use app\common\service\printer\YlyPrinterService;
  47. use app\shopapi\logic\TeamLogic;
  48. use think\facade\Db;
  49. use think\facade\Log;
  50. /**
  51. * 支付成功后处理订单状态
  52. * Class PayNotifyLogic
  53. * @package app\api\logic
  54. */
  55. class PayNotifyLogic extends BaseLogic
  56. {
  57. public static function handle($action, $orderSn, $extra = [])
  58. {
  59. //使用redis分布式锁控制并发
  60. $lockKey = 'pay_notify_' . $orderSn;
  61. $lockValue = uniqid(); // 生成唯一标识
  62. $redis = (new HandleConcurrencyCache())->getRedis();
  63. Db::startTrans();
  64. try {
  65. // 尝试加锁
  66. if ($redis !== false && !$redis->set($lockKey, $lockValue, ['NX', 'EX' => 60])) {
  67. Log::write('订单' . $orderSn . '正在处理中,跳过重复回调', 'PayNotifyLogic');
  68. return false;//订单处理中,直接返回
  69. }
  70. self::$action($orderSn, $extra);
  71. Db::commit();
  72. return true;
  73. } catch (\Exception $e) {
  74. Db::rollback();
  75. Log::write($e->__toString(), 'PayNotifyLogic');
  76. self::setError($e->getMessage());
  77. return $e->getMessage();
  78. } finally {
  79. if ($redis !== false && $redis->get($lockKey) === $lockValue) {
  80. // 释放锁
  81. $redis->del($lockKey);
  82. }
  83. }
  84. }
  85. //下单回调 //调用回调方法统一处理 更新订单支付状态
  86. private static function order($orderSn, $extra = [])
  87. {
  88. $order = Order::with(['order_goods'])->where(['sn' => $orderSn])
  89. ->lock(true) // 添加行锁
  90. ->findOrEmpty();
  91. // 检查订单状态
  92. if ($order['order_status'] != OrderEnum::STATUS_WAIT_PAY || $order['pay_status'] != PayEnum::UNPAID) {
  93. return false;
  94. }
  95. // 汽泡足迹
  96. event('Footprint', ['type' => FootprintEnum::ORDER_SETTLEMENT, 'user_id' => $order['user_id']]);
  97. //增加用户累计消费额度
  98. User::where(['id' => $order['user_id']])
  99. ->inc('total_order_amount', $order['order_amount'])
  100. ->inc('total_order_num')
  101. ->update();
  102. //赠送积分
  103. $open_award = ConfigService::get('award_integral', 'open_award', 0);
  104. if ($open_award == 1) {
  105. $award_event = ConfigService::get('award_integral', 'award_event', 0);
  106. $award_ratio = ConfigService::get('award_integral', 'award_ratio', 0);
  107. if ($award_ratio > 0) {
  108. $award_integral = floor($order['order_amount'] * ($award_ratio / 100));
  109. }
  110. }
  111. //混合支付扣减余额
  112. if($order['pay_way'] == PayEnum::MIXED_PAY) {
  113. //扣除余额
  114. User::update([
  115. 'user_money' => ['dec', $order['balance_amount']]
  116. ], ['id' => $order['user_id']]);
  117. // 记录余额流水
  118. AccountLogLogic::add(
  119. $order['user_id'],
  120. AccountLogEnum::BNW_INC_CANCEL_ORDER,
  121. AccountLogEnum::DEC,
  122. $order['balance_amount'],
  123. $order['sn'],
  124. '混合支付-余额部分'
  125. );
  126. file_put_contents(
  127. runtime_path() . 'log' . DIRECTORY_SEPARATOR . 'mixed_pay_debug_' . date('Y-m-d') . '.log',
  128. "[" . date('Y-m-d H:i:s') . "] 余额扣除成功,扣除金额: {$order['balance_amount']}" . PHP_EOL,
  129. FILE_APPEND | LOCK_EX
  130. );
  131. }
  132. //更新订单状态
  133. Order::update([
  134. 'pay_status' => PayEnum::ISPAID,
  135. 'pay_time' => time(),
  136. // 自提变为待收货
  137. 'order_status' => $order['delivery_type'] == DeliveryEnum::SELF_DELIVERY ? OrderEnum::STATUS_WAIT_RECEIVE: OrderEnum::STATUS_WAIT_DELIVERY,
  138. 'transaction_id' => $extra['transaction_id'] ?? '',
  139. 'award_integral_event' => $award_event ?? 0,
  140. 'award_integral' => $award_integral ?? 0
  141. ], ['id' => $order['id']]);
  142. // 秒杀订单更新销售数据
  143. if ($order['order_type'] == OrderEnum::SECKILL_ORDER) {
  144. $orderGoods = OrderGoods::where('order_id', $order['id'])->findOrEmpty()->toArray();
  145. if (empty($orderGoods)) {
  146. return false;
  147. }
  148. $seckillGoodsItem = SeckillGoodsItem::where([
  149. 'seckill_id' => $order['seckill_id'],
  150. 'item_id' => $orderGoods['item_id'],
  151. ])->findOrEmpty();
  152. if ($seckillGoodsItem->isEmpty()) {
  153. return false;
  154. }
  155. $seckillGoodsItem->sales_amount += $orderGoods['total_pay_price'];
  156. $seckillGoodsItem->sales_volume += $orderGoods['goods_num'];
  157. $seckillGoodsItem->closing_order ++;
  158. $seckillGoodsItem->save();
  159. }
  160. // 拼团订单数据更新
  161. if ($order['order_type'] == OrderEnum::TEAM_ORDER) {
  162. if (empty($order['order_goods'])) {
  163. return false;
  164. }
  165. $teamId = (new TeamFound())->where(['id'=>$order['team_found_id']])->value('team_id');
  166. TeamGoodsItem::update([
  167. 'sales_amount' => ['inc', $order['order_amount']],
  168. 'sales_volume' => ['inc', $order['total_num']],
  169. 'closing_order' => ['inc', 1]
  170. ], ['team_id'=>$teamId, 'item_id'=>$order['order_goods'][0]['item_id']]);
  171. }
  172. // 预售订单数据更新
  173. if ($order['order_type'] == OrderEnum::PRESELL_ORDER) {
  174. PresellGoodsItem::update([
  175. 'sale_order' => Db::raw('sale_order+1'),
  176. 'sale_money' => Db::raw('sale_money+' . $order['order_amount']),
  177. 'sale_nums' => Db::raw('sale_nums+' . $order['total_num']),
  178. ], [
  179. [ 'item_id' , '=', $order['order_goods'][0]['item_id'] ],
  180. [ 'presell_id' , '=', $order['presell_id'] ],
  181. ]);
  182. Presell::update([
  183. 'sale_order' => Db::raw('sale_order+1'),
  184. 'sale_money' => Db::raw('sale_money+' . $order['order_amount']),
  185. 'sale_nums' => Db::raw('sale_nums+' . $order['total_num']),
  186. ], [ [ 'id' , '=', $order['presell_id'] ] ]);
  187. }
  188. // 生成分销订单
  189. DistributionOrderGoodsLogic::add($order['id']);
  190. // 更新分销商等级
  191. DistributionLevelLogic::updateDistributionLevel($order['user_id']);
  192. //下架库存为零的商品
  193. // $goods_ids = OrderGoods::where('order_id',$order['id'])->column('goods_id');
  194. // Goods::update(['status'=>0],['id'=>$goods_ids,'total_stock'=>0]);
  195. //订单日志
  196. (new OrderLog())->record([
  197. 'type' => OrderLogEnum::TYPE_USER,
  198. 'channel' => OrderLogEnum::USER_PAID_ORDER,
  199. 'order_id' => $order['id'],
  200. 'operator_id' => $order['user_id']
  201. ]);
  202. // 如果是拼团订单
  203. if ($order['order_type'] == OrderEnum::TEAM_ORDER) {
  204. TeamLogic::checkTeamSuccess($order['team_found_id']);
  205. }
  206. // 消息通知 - 通知买家
  207. event('Notice', [
  208. 'scene_id' => NoticeEnum::ORDER_PAY_NOTICE,
  209. 'params' => [
  210. 'user_id' => $order['user_id'],
  211. 'order_id' => $order['id'],
  212. 'mobile' => $order['address']->mobile,
  213. ]
  214. ]);
  215. // 消息通知 - 通知卖家
  216. $mobile = ConfigService::get('shop', 'mall_contact_mobile', '');
  217. event('Notice', [
  218. 'scene_id' => NoticeEnum::SELLER_ORDER_PAY_NOTICE,
  219. 'params' => [
  220. 'mobile' => $mobile,
  221. 'order_id' => $order['id'],
  222. ]
  223. ]);
  224. //更新虚拟订单
  225. GoodsVirtualLogic::afterPayVirtualDelivery($order['id']);
  226. //更新用户等级
  227. UserLogic::updateLevel($order['user_id']);
  228. // 自动小票打印
  229. self::orderPrint($order['id']);
  230. }
  231. /**
  232. * @notes 充值成功回调
  233. * @param $orderSn
  234. * @param array $extra
  235. * @author Tab
  236. * @date 2021/8/11 14:43
  237. */
  238. public static function recharge($orderSn, $extra = [])
  239. {
  240. $order = RechargeOrder::where('sn', $orderSn)->findOrEmpty();
  241. // 增加用户累计充值金额及用户余额
  242. $user = User::findOrEmpty($order->user_id);
  243. $user->total_recharge_amount = $user->total_recharge_amount + $order->order_amount;
  244. $user->user_money = $user->user_money + $order->order_amount;
  245. $user->save();
  246. // 记录账户流水
  247. AccountLogLogic::add($order->user_id, AccountLogEnum::BNW_INC_RECHARGE, AccountLogEnum::INC, $order->order_amount, $order->sn, '用户充值');
  248. // 更新充值订单状态
  249. $order->transaction_id = $extra['transaction_id'];
  250. $order->pay_status = PayEnum::ISPAID;
  251. $order->pay_time = time();
  252. $order->save();
  253. // 充值奖励
  254. foreach($order->award as $item) {
  255. if(isset($item['give_money']) && $item['give_money'] > 0) {
  256. // 充值送余额
  257. self::awardMoney($order, $item['give_money']);
  258. }
  259. }
  260. }
  261. /**
  262. * @notes 充值送余额
  263. * @param $userId
  264. * @param $giveMoney
  265. * @author Tab
  266. * @date 2021/8/11 14:35
  267. */
  268. public static function awardMoney($order, $giveMoney)
  269. {
  270. // 充值送余额
  271. $user = User::findOrEmpty($order->user_id);
  272. $user->user_money = $user->user_money + $giveMoney;
  273. $user->save();
  274. // 记录账户流水
  275. AccountLogLogic::add($order->user_id, AccountLogEnum::BNW_INC_RECHARGE_GIVE, AccountLogEnum::INC, $giveMoney, $order->sn, '充值赠送');
  276. }
  277. /**
  278. * @notes 积分商城订单
  279. * @param $order_sn
  280. * @param array $extra
  281. * @author 段誉
  282. * @date 2022/3/31 12:13
  283. */
  284. private static function integral($order_sn, $extra = [])
  285. {
  286. $order = IntegralOrder::where(['sn' => $order_sn])->findOrEmpty();
  287. $goods = $order['goods_snap'];
  288. // 更新订单状态
  289. $data = [
  290. 'order_status' => IntegralOrderEnum::ORDER_STATUS_DELIVERY,
  291. 'pay_status' => PayEnum::ISPAID,
  292. 'pay_time' => time(),
  293. ];
  294. // 红包类型 或者 无需物流 支付完即订单完成
  295. if ($goods['type'] == IntegralGoodsEnum::TYPE_BALANCE || $goods['delivery_way'] == IntegralGoodsEnum::DELIVERY_NO_EXPRESS) {
  296. $data['order_status'] = IntegralOrderEnum::ORDER_STATUS_COMPLETE;
  297. $data['confirm_time'] = time();
  298. }
  299. // 第三方流水号
  300. if (isset($extra['transaction_id'])) {
  301. $data['transaction_id'] = $extra['transaction_id'];
  302. }
  303. IntegralOrder::update($data, ['id' => $order['id']]);
  304. // 更新商品销量
  305. IntegralGoods::where([['id', '=', $goods['id']], ['stock', '>=', $order['total_num']]])
  306. ->dec('stock', $order['total_num'])
  307. ->inc('sales', $order['total_num'])
  308. ->update();
  309. // 红包类型,直接增加余额
  310. if ($goods['type'] == IntegralGoodsEnum::TYPE_BALANCE) {
  311. $reward = round($goods['balance'] * $order['total_num'], 2);
  312. User::where(['id' => $order['user_id']])
  313. ->inc('user_money', $reward)
  314. ->update();
  315. AccountLogLogic::add(
  316. $order['user_id'],
  317. AccountLogEnum::BNW_INC_INTEGRAL_ORDER,
  318. AccountLogEnum::INC, $reward, $order['sn']
  319. );
  320. }
  321. }
  322. /**
  323. * @notes 小票打印
  324. * @param $orderId
  325. * @author Tab
  326. * @date 2021/11/17 9:53
  327. */
  328. public static function orderPrint($orderId)
  329. {
  330. try {
  331. $order = self::getOrderInfo($orderId);
  332. (new YlyPrinterService())->startPrint($order, PrinterEnum::ORDER_PAY);
  333. } catch (\Exception $e) {
  334. self::handleCatch($e);
  335. }
  336. }
  337. public static function getOrderInfo($orderId)
  338. {
  339. $field = [
  340. 'id',
  341. 'sn',
  342. 'order_type',
  343. 'pay_way',
  344. 'delivery_type',
  345. 'goods_price',
  346. 'order_amount',
  347. 'discount_amount',
  348. 'express_price',
  349. 'user_remark',
  350. 'pickup_code',
  351. 'address',
  352. 'selffetch_shop_id',
  353. 'create_time',
  354. 'pickup_code',
  355. 'change_price',
  356. 'member_amount',
  357. ];
  358. $order = Order::field($field)->with(['orderGoods' => function($query) {
  359. $query->field(['goods_num', 'order_id', 'goods_price', 'goods_snap', 'original_price' ]);
  360. }])
  361. ->append(['delivery_address', 'pay_way_desc', 'delivery_type_desc'])
  362. ->findOrEmpty($orderId);
  363. if ($order->isEmpty()) {
  364. throw new \Exception("订单不存在");
  365. }
  366. // 门店自提
  367. if ($order->delivery_type == DeliveryEnum::SELF_DELIVERY) {
  368. $field = [
  369. 'id',
  370. 'name',
  371. 'contact',
  372. 'province',
  373. 'city',
  374. 'district',
  375. 'address',
  376. ];
  377. $selffetchShop = SelffetchShop::field($field)
  378. ->append(['detailed_address'])
  379. ->findOrEmpty($order->selffetch_shop_id);
  380. $order->selffetch_shop = $selffetchShop;
  381. }
  382. return $order->toArray();
  383. }
  384. /**
  385. * @notes 处理易联云异常
  386. * @param $e
  387. * @return false
  388. * @author Tab
  389. * @date 2021/11/17 10:01
  390. */
  391. public static function handleCatch($e)
  392. {
  393. $msg = json_decode($e->getMessage(),true);
  394. if(18 === $e->getCode()){
  395. //access_token过期,清除缓存中的access_token
  396. (new YlyPrinterCache())->deleteTag();
  397. };
  398. if($msg && isset($msg['error'])){
  399. Log::write('小票打印出错1:易联云'.$msg['error_description']);
  400. }
  401. Log::write('小票打印出错2:'.$e->getMessage());
  402. }
  403. }