You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

412 lines
16 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. <?php
  2. namespace App\Controller;
  3. use App\Constants\LogLabel;
  4. use App\Model\Goods;
  5. use App\Model\Order;
  6. use App\Model\OrderGoods;
  7. use App\Model\OrderMain;
  8. use App\Model\OrderSalesStatistic;
  9. use App\Model\SpecCombination;
  10. use App\Model\Store;
  11. use App\Model\StoreAccount;
  12. use App\Model\SystemConfig;
  13. use App\Model\Users;
  14. use App\Service\CouponRebateServiceInterface;
  15. use App\Service\DeviceServiceInterface;
  16. use App\Service\FeiePrintServiceInterface;
  17. use App\Service\MiniprogramServiceInterface;
  18. use App\Service\MqttServiceInterface;
  19. use App\Service\UserServiceInterface;
  20. use EasyWeChat\Factory;
  21. use Hyperf\DbConnection\Db;
  22. use Hyperf\Guzzle\CoroutineHandler;
  23. use Exception;
  24. use Hyperf\Di\Annotation\Inject;
  25. use Hyperf\HttpMessage\Stream\SwooleStream;
  26. use Symfony\Component\HttpFoundation\Request;
  27. class NotifyController extends BaseController
  28. {
  29. const AWARD_LIMIT_AMOUNT = 3;
  30. /**
  31. * @Inject
  32. * @var MqttServiceInterface
  33. */
  34. protected $mqttSpeakerService;
  35. /**
  36. * @Inject
  37. * @var DeviceServiceInterface
  38. */
  39. protected $deviceService;
  40. /**
  41. * @Inject
  42. * @var MiniprogramServiceInterface
  43. */
  44. protected $miniprogramService;
  45. /**
  46. * @Inject
  47. * @var FeiePrintServiceInterface
  48. */
  49. protected $feiePrintService;
  50. /**
  51. * @Inject
  52. * @var UserServiceInterface
  53. */
  54. protected $userService;
  55. /**
  56. * @Inject
  57. * @var CouponRebateServiceInterface
  58. */
  59. protected $couponRebateService;
  60. public function wxminiOnline()
  61. {
  62. $config = config('wxpay');
  63. $app = Factory::payment($config);
  64. $app['guzzle_handler'] = CoroutineHandler::class;
  65. $get = $this->request->getQueryParams();
  66. $post = $this->request->getParsedBody();
  67. $cookie = $this->request->getCookieParams();
  68. $files = $this->request->getUploadedFiles();
  69. $server = $this->request->getServerParams();
  70. $xml = $this->request->getBody()->getContents();
  71. $app['request'] = new Request($get,$post,[],$cookie,$files,$server,$xml);
  72. // 通知回调,进行业务处理
  73. $response = $app->handlePaidNotify(function ($message, $fail) use ($app) {
  74. Db::beginTransaction();
  75. try {
  76. // 支付失败或者通知失败
  77. if (
  78. empty($message)
  79. || $message['return_code'] != 'SUCCESS'
  80. || !isset($message['result_code'])
  81. || $message['result_code'] != 'SUCCESS'
  82. ) {
  83. $this->log->event(
  84. LogLabel::PAY_NOTIFY_WXMINI,
  85. $message
  86. );
  87. Db::rollBack();
  88. $fail('Unknown error but FAIL');
  89. }
  90. // 查询订单
  91. $orderMain = OrderMain::query()
  92. ->where([
  93. 'global_order_id' => $message['out_trade_no'],
  94. 'type' => OrderMain::ORDER_TYPE_ONLINE
  95. ])
  96. ->first();
  97. // 订单不存在
  98. if (empty($orderMain)) {
  99. $this->log->event(
  100. LogLabel::PAY_NOTIFY_WXMINI,
  101. ['global_order_id_fail' => $message['out_trade_no']]
  102. );
  103. Db::rollBack();
  104. return true;
  105. }
  106. // 修改订单、子订单状态
  107. $currentTime = time();
  108. $orderMain->state = OrderMain::ORDER_STATE_UNTAKE;
  109. $orderMain->time_pay = $currentTime;
  110. $orderMain->pay_time = date('Y-m-d H:i:s', $currentTime);
  111. $orderMain->save();
  112. $upOrder = Order::query()
  113. ->where(['order_main_id' => $orderMain->id])
  114. ->update(['state' => OrderMain::ORDER_STATE_UNTAKE, 'pay_time' => $orderMain->pay_time]);
  115. // 更新商户销量
  116. $upStoreScore = Store::query()
  117. ->whereIn('id', explode(',', $orderMain->store_ids))
  118. ->update(['score' => Db::raw('score+1')]);
  119. // 更新商品库存和销量
  120. $orders = Order::query()->select(['id', 'money', 'user_id', 'store_id', 'pay_time'])
  121. ->where(['order_main_id' => $orderMain->id])
  122. ->get()
  123. ->toArray();
  124. $orderGoods = OrderGoods::query()->select(['good_id AS id', 'number', 'combination_id'])
  125. ->whereIn('order_id', array_values(array_column($orders, 'id')))
  126. ->get()
  127. ->toArray();
  128. foreach ($orderGoods as $key => &$goodsItem) {
  129. $goods = Goods::find($goodsItem['id']);
  130. // 库存处理,有规格
  131. if ($goodsItem['combination_id']) {
  132. $combination = SpecCombination::find($goodsItem['combination_id']);
  133. $combination->number = $combination->number - $goodsItem['number'];
  134. $combination->save();
  135. } else {
  136. $goods->inventory = $goods->inventory - $goodsItem['number'];
  137. }
  138. $goods->sales = $goods->sales - $goodsItem['number'];
  139. $goods->save();
  140. }
  141. // 月销流水
  142. $statistics = [];
  143. foreach ($orders as $key => &$order) {
  144. $statistics[] = [
  145. 'money' => $order['money'],
  146. 'user_id' => $order['user_id'],
  147. 'store_id' => $order['store_id'],
  148. 'market_id' => $orderMain->market_id,
  149. 'order_id' => $order['id'],
  150. 'createtime' => strtotime($order['pay_time']),
  151. ];
  152. }
  153. if (is_array($statistics) && !empty($statistics)) {
  154. $inSalesStatistics = OrderSalesStatistic::query()->insert($statistics);
  155. }
  156. // 优惠券返券
  157. $this->couponRebateService->couponRebateInTask($orderMain->id);
  158. // 喇叭通知,兼容旧音响,MQTT+IOT
  159. $res = $this->mqttSpeakerService->speakToStore($orderMain->id);
  160. $this->log->event(
  161. LogLabel::PAY_NOTIFY_WXMINI,
  162. ['fail_mqtt' => json_encode($res)]
  163. );
  164. $res = $this->deviceService->pubMsgToStoreByOrderMainId($orderMain->id);
  165. $this->log->event(
  166. LogLabel::PAY_NOTIFY_WXMINI,
  167. ['fail_device' => json_encode($res)]
  168. );
  169. // 公众号模板消息
  170. $res = $this->miniprogramService->sendTemMsgForOnlineOrder($orderMain->id);
  171. $this->log->event(
  172. LogLabel::PAY_NOTIFY_WXMINI,
  173. ['fail_mini' => json_encode($res)]
  174. );
  175. // 打印订单,自动打印 TODO 后续优化调用逻辑
  176. $res = $this->feiePrintService->feiePrint($orderMain->global_order_id);
  177. $this->log->event(
  178. LogLabel::PAY_NOTIFY_WXMINI,
  179. ['fail_feie' => json_encode($res)]
  180. );
  181. Db::commit();
  182. return true;
  183. } catch (Exception $e) {
  184. $this->log->event(
  185. LogLabel::PAY_NOTIFY_WXMINI,
  186. ['exception_fail' => $e->getMessage()]
  187. );
  188. Db::rollBack();
  189. $fail('Exception');
  190. }
  191. });
  192. return $this->response
  193. ->withHeader('Content-Type', 'text/xml')
  194. ->withStatus(200)
  195. ->withBody(new SwooleStream($response->getContent()));
  196. }
  197. public function wxminiOffline()
  198. {
  199. $config = config('wxpay');
  200. $app = Factory::payment($config);
  201. $app['guzzle_handler'] = CoroutineHandler::class;
  202. $get = $this->request->getQueryParams();
  203. $post = $this->request->getParsedBody();
  204. $cookie = $this->request->getCookieParams();
  205. $files = $this->request->getUploadedFiles();
  206. $server = $this->request->getServerParams();
  207. $xml = $this->request->getBody()->getContents();
  208. $app['request'] = new Request($get,$post,[],$cookie,$files,$server,$xml);
  209. // 通知回调,进行业务处理
  210. $response = $app->handlePaidNotify(function ($message, $fail) use ($app) {
  211. Db::beginTransaction();
  212. try {
  213. // 支付失败或者通知失败
  214. if (
  215. empty($message)
  216. || $message['return_code'] != 'SUCCESS'
  217. || !isset($message['result_code'])
  218. || $message['result_code'] != 'SUCCESS'
  219. ) {
  220. $this->log->event(
  221. LogLabel::PAY_NOTIFY_WXMINI,
  222. $message
  223. );
  224. Db::rollBack();
  225. $fail('Unknown error but FAIL');
  226. }
  227. // 查询订单
  228. $orderMain = OrderMain::query()
  229. ->where([
  230. 'global_order_id' => $message['out_trade_no'],
  231. 'type' => OrderMain::ORDER_TYPE_OFFLINE
  232. ])
  233. ->first();
  234. // 订单不存在
  235. if (empty($orderMain)) {
  236. $this->log->event(
  237. LogLabel::PAY_NOTIFY_WXMINI,
  238. ['global_order_id_fail' => $message['out_trade_no']]
  239. );
  240. Db::rollBack();
  241. return true;
  242. }
  243. // 修改订单、子订单状态
  244. $currentTime = time();
  245. $orderMain->state = OrderMain::ORDER_STATE_UNTAKE;
  246. $orderMain->dm_state = OrderMain::ORDER_STATE_UNTAKE;
  247. $orderMain->time_pay = $currentTime;
  248. $orderMain->pay_time = date('Y-m-d H:i:s', $currentTime);
  249. $orderMain->save();
  250. $upOrder = Order::query()
  251. ->where(['order_main_id' => $orderMain->id])
  252. ->update([
  253. 'state' => OrderMain::ORDER_STATE_UNTAKE,
  254. 'dm_state' => OrderMain::ORDER_STATE_UNTAKE,
  255. 'pay_time' => date('Y-m-d H:i:s', $currentTime)
  256. ]);
  257. // 查询子订单,当面付目前实际上只有一个子订单
  258. $orders = Order::query()->select(['id', 'money', 'user_id', 'store_id', 'pay_time'])
  259. ->where(['order_main_id' => $orderMain->id])
  260. ->get()
  261. ->toArray();
  262. // 商户钱包、流水资金、奖励、发布模板消息处理
  263. foreach ($orders as $key => $orderItem) {
  264. $recordBase = [
  265. 'user_id' => $orderItem['user_id'],
  266. 'order_id' => $orderItem['id'],
  267. 'store_id' => $orderItem['store_id'],
  268. 'type' => 1,
  269. 'time' => date('Y-m-d H:i:s', $currentTime),
  270. 'add_time' => $currentTime,
  271. ];
  272. // 钱包
  273. $store = Store::find($orderItem['store_id']);
  274. $store->store_wallet = bcadd($store->store_wallet, $orderItem['money'], 2);
  275. $store->save();
  276. // 流水
  277. $record = [
  278. 'money' => $orderItem['money'],
  279. 'note' => '当面付订单收入',
  280. 'category' => 2,
  281. ];
  282. StoreAccount::query()->insert(array_merge($recordBase, $record));
  283. // 平台新用户奖励给商户
  284. $isStageNewUser = $this->userService->isStageNewUser($orderItem['user_id'], $orderMain->id);
  285. $needAward = false;
  286. $awardAmount = 0;
  287. if ($isStageNewUser) {
  288. $awardAmount = SystemConfig::query()->where(['type' => 1, 'menu_name' => 'award_new_user'])->value('value');
  289. // 流水
  290. $record = [
  291. 'money' => $awardAmount,
  292. 'note' => '新用户下单成功,平台奖励',
  293. 'category' => 3,
  294. ];
  295. $needAward = true;
  296. } else {
  297. $isStoreFirstOrderToday = $this->userService->isStoreFirstOrderToday($orderItem['user_id'],$orderItem['store_id'],$orderItem['id'], self::AWARD_LIMIT_AMOUNT);
  298. if ($isStoreFirstOrderToday && $orderItem['money'] >= self::AWARD_LIMIT_AMOUNT) {
  299. $awardAmount = SystemConfig::query()->where(['type' => 1, 'menu_name' => 'award_each_order'])->value('value');
  300. // 流水
  301. $record = [
  302. 'money' => $awardAmount,
  303. 'note' => '用户下单成功,平台奖励',
  304. 'category' => 4,
  305. ];
  306. $needAward = true;
  307. }
  308. }
  309. if ($needAward && $awardAmount) {
  310. // 奖励钱包
  311. $store->refresh();
  312. $store->award_money = bcadd($store->award_money, $awardAmount, 2);
  313. $store->save();
  314. // 流水
  315. StoreAccount::query()->insert(array_merge($recordBase, $record));
  316. // 发布公众号消息
  317. $openid = Users::query()->where(['id' => $store['user_id']])->value('openid');
  318. $res = $this->miniprogramService->sendTemMsgForAward($record['money'], $record['note'], $openid, $recordBase['time']);
  319. }
  320. }
  321. // 喇叭通知,兼容旧音响,MQTT+IOT
  322. $res = $this->mqttSpeakerService->speakToStore($orderMain->id);
  323. $this->log->event(
  324. LogLabel::PAY_NOTIFY_WXMINI,
  325. ['fail_mqtt' => json_encode($res)]
  326. );
  327. $res = $this->deviceService->pubMsgToStoreByOrderMainId($orderMain->id);
  328. $this->log->event(
  329. LogLabel::PAY_NOTIFY_WXMINI,
  330. ['fail_device' => json_encode($res)]
  331. );
  332. // 公众号模板消息
  333. $res = $this->miniprogramService->sendTemMsgForOfflineOrder($orderMain->id);
  334. $this->log->event(
  335. LogLabel::PAY_NOTIFY_WXMINI,
  336. ['fail_mini' => json_encode($res)]
  337. );
  338. Db::commit();
  339. return true;
  340. } catch (Exception $e) {
  341. $this->log->event(
  342. LogLabel::PAY_NOTIFY_WXMINI,
  343. ['exception_fail' => $e->getMessage()]
  344. );
  345. Db::rollBack();
  346. $fail('Exception');
  347. }
  348. });
  349. return $this->response
  350. ->withHeader('Content-Type', 'text/xml')
  351. ->withStatus(200)
  352. ->withBody(new SwooleStream($response->getContent()));
  353. }
  354. }