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.

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