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.

368 lines
14 KiB

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