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.

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