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.

379 lines
14 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
  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. $res = $this->deviceService->pubMsgToStoreByOrderMainId($orderMain->id);
  153. // 公众号模板消息
  154. $res = $this->miniprogramService->sendTemMsgForOnlineOrder($orderMain->id);
  155. // 打印订单,自动打印 TODO 后续优化调用逻辑
  156. $res = $this->feiePrintService->feiePrint($orderMain->global_order_id);
  157. Db::commit();
  158. return true;
  159. } catch (Exception $e) {
  160. $this->log->event(
  161. LogLabel::PAY_NOTIFY_WXMINI,
  162. ['exception_fail' => $e->getMessage()]
  163. );
  164. Db::rollBack();
  165. $fail('Exception');
  166. }
  167. });
  168. return $this->response
  169. ->withHeader('Content-Type', 'text/xml')
  170. ->withStatus(200)
  171. ->withBody(new SwooleStream($response->getContent()));
  172. }
  173. public function wxminiOffline()
  174. {
  175. $config = config('wxpay');
  176. $app = Factory::payment($config);
  177. $app['guzzle_handler'] = CoroutineHandler::class;
  178. $get = $this->request->getQueryParams();
  179. $post = $this->request->getParsedBody();
  180. $cookie = $this->request->getCookieParams();
  181. $files = $this->request->getUploadedFiles();
  182. $server = $this->request->getServerParams();
  183. $xml = $this->request->getBody()->getContents();
  184. $app['request'] = new Request($get,$post,[],$cookie,$files,$server,$xml);
  185. // 通知回调,进行业务处理
  186. $response = $app->handlePaidNotify(function ($message, $fail) use ($app) {
  187. Db::beginTransaction();
  188. try {
  189. // 支付失败或者通知失败
  190. if (
  191. empty($message)
  192. || $message['return_code'] != 'SUCCESS'
  193. || !isset($message['result_code'])
  194. || $message['result_code'] != 'SUCCESS'
  195. ) {
  196. $this->log->event(
  197. LogLabel::PAY_NOTIFY_WXMINI,
  198. $message
  199. );
  200. Db::rollBack();
  201. $fail('Unknown error but FAIL');
  202. }
  203. // 查询订单
  204. $orderMain = OrderMain::query()
  205. ->where([
  206. 'global_order_id' => $message['out_trade_no'],
  207. 'type' => OrderMain::ORDER_TYPE_OFFLINE
  208. ])
  209. ->first();
  210. // 订单不存在
  211. if (empty($orderMain)) {
  212. $this->log->event(
  213. LogLabel::PAY_NOTIFY_WXMINI,
  214. ['global_order_id_fail' => $message['out_trade_no']]
  215. );
  216. Db::rollBack();
  217. return true;
  218. }
  219. // 修改订单、子订单状态
  220. $currentTime = time();
  221. $orderMain->state = OrderMain::ORDER_STATE_UNTAKE;
  222. $orderMain->dm_state = OrderMain::ORDER_STATE_UNTAKE;
  223. $orderMain->time_pay = $currentTime;
  224. $orderMain->pay_time = date('Y-m-d H:i:s', $currentTime);
  225. $orderMain->save();
  226. $upOrder = Order::query()
  227. ->where(['order_main_id' => $orderMain->id])
  228. ->update([
  229. 'state' => OrderMain::ORDER_STATE_UNTAKE,
  230. 'dm_state' => OrderMain::ORDER_STATE_UNTAKE,
  231. 'pay_time' => date('Y-m-d H:i:s', $currentTime)
  232. ]);
  233. // 查询子订单,当面付目前实际上只有一个子订单
  234. $orders = Order::query()->select(['id', 'money', 'user_id', 'store_id', 'pay_time'])
  235. ->where(['order_main_id' => $orderMain->id])
  236. ->get()
  237. ->toArray();
  238. // 商户钱包、流水资金、奖励、发布模板消息处理
  239. foreach ($orders as $key => $orderItem) {
  240. $recordBase = [
  241. 'user_id' => $orderItem['user_id'],
  242. 'order_id' => $orderItem['id'],
  243. 'store_id' => $orderItem['store_id'],
  244. 'type' => 1,
  245. 'time' => date('Y-m-d H:i:s', $currentTime),
  246. 'add_time' => $currentTime,
  247. ];
  248. // 钱包
  249. $store = Store::find($orderItem['store_id']);
  250. $store->store_wallet = bcadd($store->store_wallet, $orderItem['money'], 2);
  251. $store->save();
  252. // 流水
  253. $record = [
  254. 'money' => $orderItem['money'],
  255. 'note' => '当面付订单收入',
  256. 'category' => 2,
  257. ];
  258. StoreAccount::query()->insert(array_merge($recordBase, $record));
  259. // 平台新用户奖励给商户
  260. $isStageNewUser = $this->userService->isStageNewUser($orderItem['user_id'], $orderMain->id);
  261. $needAward = false;
  262. $awardAmount = 0;
  263. if ($isStageNewUser) {
  264. $awardAmount = SystemConfig::query()->where(['type' => 1, 'menu_name' => 'award_new_user'])->value('value');
  265. // 流水
  266. $record = [
  267. 'money' => $awardAmount,
  268. 'note' => '新用户下单成功,平台奖励',
  269. 'category' => 3,
  270. ];
  271. $needAward = true;
  272. } else {
  273. $isStoreFirstOrderToday = $this->userService->isStoreFirstOrderToday($orderItem['user_id'],$orderItem['store_id'],$orderItem['id'], self::AWARD_LIMIT_AMOUNT);
  274. if ($isStoreFirstOrderToday && $orderItem['money'] >= self::AWARD_LIMIT_AMOUNT) {
  275. $awardAmount = SystemConfig::query()->where(['type' => 1, 'menu_name' => 'award_each_order'])->value('value');
  276. // 流水
  277. $record = [
  278. 'money' => $awardAmount,
  279. 'note' => '用户下单成功,平台奖励',
  280. 'category' => 4,
  281. ];
  282. $needAward = true;
  283. }
  284. }
  285. if ($needAward && $awardAmount) {
  286. // 奖励钱包
  287. $store->refresh();
  288. $store->award_money = bcadd($store->award_money, $awardAmount, 2);
  289. $store->save();
  290. // 流水
  291. StoreAccount::query()->insert(array_merge($recordBase, $record));
  292. // 发布公众号消息
  293. $openid = Users::query()->where(['id' => $store['user_id']])->value('openid');
  294. $res = $this->miniprogramService->sendTemMsgForAward($record['money'], $record['note'], $openid, $recordBase['time']);
  295. }
  296. }
  297. // 喇叭通知,兼容旧音响,MQTT+IOT
  298. $res = $this->mqttSpeakerService->speakToStore($orderMain->id);
  299. $res = $this->deviceService->pubMsgToStoreByOrderMainId($orderMain->id);
  300. // 公众号模板消息
  301. $res = $this->miniprogramService->sendTemMsgForOfflineOrder($orderMain->id);
  302. Db::commit();
  303. return true;
  304. } catch (Exception $e) {
  305. $this->log->event(
  306. LogLabel::PAY_NOTIFY_WXMINI,
  307. ['exception_fail' => $e->getMessage()]
  308. );
  309. Db::rollBack();
  310. $fail('Exception');
  311. }
  312. });
  313. return $this->response
  314. ->withHeader('Content-Type', 'text/xml')
  315. ->withStatus(200)
  316. ->withBody(new SwooleStream($response->getContent()));
  317. }
  318. }