海南旅游SAAS
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.

432 lines
14 KiB

4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. <?php
  2. namespace App\Http\Controllers\Api;
  3. use App\Common\PayType;
  4. use App\Jobs\BalanceDue;
  5. use App\Jobs\OrderTimeout;
  6. use App\Models\AdminSetting;
  7. use App\Models\Agent;
  8. use App\Models\AgentProduct;
  9. use App\Models\AgentSetting;
  10. use App\Models\IndustryOrder;
  11. use App\Models\IndustryPayLog;
  12. use App\Models\IndustryProduct;
  13. use App\Models\Order;
  14. use App\Models\Product;
  15. use App\Models\SettledOrder;
  16. use App\Models\Supplier;
  17. use App\Models\UserMoneyLog;
  18. use EasyWeChat\Factory;
  19. use EasyWeChat\Kernel\Exceptions\Exception;
  20. use EasyWeChat\Payment\Kernel\Exceptions\InvalidSignException;
  21. use Illuminate\Support\Facades\DB;
  22. use App\Common\OrderStatus;
  23. use Illuminate\Support\Facades\Log;
  24. class WxpayController
  25. {
  26. private $app = null;
  27. public function __construct()
  28. {
  29. $setting = AdminSetting::val(['payee_appid', 'payee_mchid', 'payee_mchkey']);
  30. if (!isset($setting['payee_appid'], $setting['payee_mchid'], $setting['payee_mchkey'])) {
  31. return '获取系统配置失败';
  32. }
  33. $config = [
  34. 'app_id' => $setting['payee_appid'],
  35. 'mch_id' => $setting['payee_mchid'],
  36. 'key' => $setting['payee_mchkey'],
  37. ];
  38. $this->app = Factory::payment($config);
  39. }
  40. //微信支付 支付结果通知网址
  41. public function notify()
  42. {
  43. $agent_id = request()->route('agent_id');
  44. // $agent = Agent::find($agent_id);
  45. try {
  46. $response = $this->app->handlePaidNotify(function ($message, $fail) use ($agent_id) {
  47. //仅测试用,回调记录
  48. DB::table('pay_debugs')->insert(['agent_id' => $agent_id, 'type' => 1, 'content' => json_encode($message)]);
  49. // $this->log($message);
  50. // 请求成功
  51. if ($message['return_code'] === 'SUCCESS') {
  52. //主要是为了区分定金支付和尾款支付,订单号带有-status后缀,分割后前面才是真正的订单号
  53. $order_no = explode('-', $message['out_trade_no'])[0];
  54. $order = Order::query()
  55. ->where(['order_no' => $order_no])
  56. ->first();
  57. //已经处理过的订单直接返回true
  58. if ($order && in_array($order->status, [OrderStatus::PAID, OrderStatus::PAID_RETAINAGE, OrderStatus::SUCCESS])) {
  59. return true;
  60. }
  61. //判断该微信支付订单号有没有处理过
  62. $exist_log = UserMoneyLog::where([
  63. 'user_id' => $order->user_id,
  64. 'order_id' => $order->id,
  65. 'type' => 1,
  66. 'transaction_id' => $message['transaction_id'],
  67. ])->first();
  68. if ($exist_log) {
  69. return true;
  70. }
  71. // 支付成功
  72. if ($message['result_code'] === 'SUCCESS') {
  73. DB::beginTransaction();
  74. try {
  75. //增加销量,库存在拍下时已经减了
  76. $agent_product = AgentProduct::find($order->agent_product_id);
  77. $agent_product->increment('sale', $order->num);
  78. Product::query()
  79. ->where('id', $order->product_id)
  80. ->increment('sale', $order->num);
  81. $status = $order->status;
  82. $pay_type = $order->pay_type;
  83. $money = $message['total_fee'] / 100;
  84. //定金支付和首付款支付
  85. if (in_array($pay_type, [PayType::DEPOSIT_PAY, PayType::EARNEST_PAY, PayType::DOWN_PAYMENT])) {
  86. if ($status == OrderStatus::UNPAID) {
  87. $order->status = OrderStatus::PAY_EARNEST;
  88. } else if ($status == OrderStatus::PAY_EARNEST) {
  89. $order->status = OrderStatus::PAID_RETAINAGE;
  90. $order->verify_code = uniqid(); //生成核销码
  91. }
  92. } else if ($pay_type == PayType::ONLINE) {
  93. $order->status = OrderStatus::PAID;
  94. $order->verify_code = uniqid(); //生成核销码
  95. }
  96. $order->paid_at = now();
  97. $order->paid_money = DB::raw('`paid_money` + ' . $money);
  98. //如果是已付定金,重新设置超时时间,否则清除超时时间
  99. if ($order->status == OrderStatus::PAY_EARNEST) {
  100. if ($pay_type == PayType::DEPOSIT_PAY || $pay_type == PayType::EARNEST_PAY) { //订金超时
  101. $time = $order->prepay_timeout * 60;
  102. }
  103. if (empty($time)) { //默认订单超时
  104. $time = (AgentSetting::val($agent_id, 'order_timeout') ?? 60) * 60;
  105. }
  106. $order->timeout = date('Y-m-d H:i:s', time() + $time);
  107. //订单超时
  108. OrderTimeout::dispatch($order->order_no,$time);
  109. //尾款通知时间 默认为剩余三小时自动通知
  110. $smsEarnest = env('SMS_EARNEST',60*60*24*3);
  111. //短信通知
  112. if(env('SMS_SWITCH' , '') == true && $time > $smsEarnest) {
  113. //如果剩余时间大于三小时 在订单到期前三小时给用户发短信
  114. if ($time > $smsEarnest) {
  115. BalanceDue::dispatch($order->order_no,$time - $smsEarnest);
  116. }
  117. }
  118. } else {
  119. $order->timeout = null;
  120. }
  121. $order->save();
  122. //资金流水
  123. UserMoneyLog::query()->create([
  124. 'user_id' => $order->user_id,
  125. 'agent_id' => $order->agent_id,
  126. 'money' => $money,
  127. 'order_id' => $order->id,
  128. 'type' => 1,
  129. 'desc' => DB::raw("LEFT('购买产品:{$order->title}', 250)"),
  130. 'transaction_id' => $message['transaction_id'], //微信支付订单号
  131. 'created_at' => now(), //模型没有updated_at,无法自动写入时间
  132. 'out_trade_no' => $message['out_trade_no'] ?? '',
  133. ]);
  134. DB::commit();
  135. return true;
  136. } catch (Exception $e) {
  137. DB::rollBack();
  138. $fail('Unknown error');
  139. }
  140. } // 支付失败
  141. else if ($message['result_code'] === 'FAIL') {
  142. return true;
  143. }
  144. }
  145. // 希望微信重试
  146. $fail('Unknown error 2');
  147. });
  148. } catch (InvalidSignException | Exception | \Exception $e) {
  149. $this->log($e->getMessage() . $e->getFile() . $e->getLine());
  150. return 'error';
  151. }
  152. return $response;
  153. }
  154. //退款通知
  155. public function refund()
  156. {
  157. $agent_id = request()->route('agent_id');
  158. // $agent = Agent::find($agent_id);
  159. try {
  160. $response = $this->app->handleRefundedNotify(function ($message, $reqInfo, $fail) use ($agent_id) {
  161. //仅测试用,回调记录
  162. DB::table('pay_debugs')->insert(['agent_id' => $agent_id, 'type' => 2, 'content' => json_encode($message)]);
  163. // 记录一下本地调试
  164. // $this->log(['message' => $message, 'reqInfo' => $reqInfo], 'refund');
  165. // 请求成功
  166. if ($message['return_code'] === 'SUCCESS') {
  167. //主要是为了区分定金支付和尾款支付,订单号带有-status后缀,分割后前面才是真正的订单号
  168. $order_no = explode('-', $reqInfo['out_trade_no'])[0];
  169. $order = Order::query()
  170. ->where(['order_no' => $order_no])
  171. ->first();
  172. //如果已经处理过则不再处理
  173. if ($order->status == OrderStatus::REFUNDED) {
  174. return true;
  175. }
  176. //如果已经存在相关的退款记录,也不再处理
  177. $exist_log = UserMoneyLog::where([
  178. 'user_id' => $order->user_id,
  179. 'order_id' => $order->id,
  180. 'type' => 2,
  181. 'transaction_id' => $reqInfo['transaction_id'],
  182. ])->first();
  183. if ($exist_log) {
  184. return true;
  185. }
  186. $log = UserMoneyLog::query()
  187. ->where([
  188. 'user_id' => $order->user_id,
  189. 'order_id' => $order->id,
  190. 'type' => 1,
  191. 'transaction_id' => $reqInfo['transaction_id'],
  192. ])->first();
  193. if (!$log) {
  194. $fail('找不到交易信息');
  195. }
  196. // 退款成功
  197. if ($reqInfo['refund_status'] === 'SUCCESS') {
  198. DB::beginTransaction();
  199. try {
  200. //更新订单状态为退款成功
  201. $order->status = OrderStatus::REFUNDED;
  202. $order->save();
  203. //记录日志
  204. UserMoneyLog::create([
  205. 'user_id' => $order->user_id,
  206. 'agent_id' => $order->agent_id,
  207. 'money' => -$reqInfo['settlement_refund_fee'] / 100,
  208. 'order_id' => $order->id,
  209. 'type' => 2,
  210. 'desc' => DB::raw("LEFT('退款:{$order->title}', 250)"),
  211. 'transaction_id' => $reqInfo['transaction_id'],
  212. 'created_at' => now(), //模型没有updated_at,无法自动写入时间
  213. ]);
  214. // 退库存
  215. Product::query()
  216. ->where('id', $order->product_id)
  217. ->increment('stock', $order->num);
  218. DB::commit();
  219. return true;
  220. } catch (\Exception $e) {
  221. DB::rollBack();
  222. $fail('Unknown error');
  223. }
  224. }
  225. }
  226. $fail('Unknown error 2');
  227. });
  228. } catch (Exception | \Exception $e) {
  229. $this->log($e->getMessage() . $e->getFile() . $e->getLine());
  230. return 'error';
  231. }
  232. return $response;
  233. }
  234. //保存消息,用于调试
  235. private function log($write_data, $type = 'notify')
  236. {
  237. $dir = storage_path('wxpay');
  238. if (!is_dir($dir)) {
  239. mkdir($dir);
  240. }
  241. $filename = $dir . '/' . $type . '_' . date('Y-m-d-H') . '.log';
  242. $data = '[' . date('Y-m-d H:i:s') . ']' . PHP_EOL;
  243. $data .= '[message]: ' . (is_array($write_data) ? json_encode($write_data) : $write_data) . PHP_EOL . PHP_EOL;
  244. file_put_contents($filename, $data, FILE_APPEND);
  245. }
  246. /**
  247. * 行业产品支付回调
  248. */
  249. public function IndustryProductNotify()
  250. {
  251. try {
  252. $response = $this->app->handlePaidNotify(function ($message, $fail) {
  253. //仅测试用,回调记录
  254. DB::table('pay_debugs')->insert(['agent_id' => -1, 'type' => 1, 'content' => json_encode($message)]);
  255. // 请求成功
  256. if ($message['return_code'] === 'SUCCESS') {
  257. //主要是为了区分定金支付和尾款支付,订单号带有-status后缀,分割后前面才是真正的订单号
  258. $order_no = explode('-', $message['out_trade_no'])[0];
  259. $order = IndustryOrder::query()
  260. ->where(['order_no' => $order_no])
  261. ->first();
  262. //已经处理过的订单直接返回true
  263. if ($order && in_array($order->status, [OrderStatus::PAID, OrderStatus::PAID_RETAINAGE, OrderStatus::SUCCESS])) {
  264. return true;
  265. }
  266. //判断该微信支付订单号有没有处理过
  267. $exist_log = IndustryPayLog::where([
  268. 'agent_id' => $order->agent_id,
  269. 'supplier_id' => $order->supplier_id,
  270. 'industry_order_id' => $order->id,
  271. 'type' => 1,
  272. 'transaction_id' => $message['transaction_id'],
  273. ])->first();
  274. if ($exist_log) {
  275. return true;
  276. }
  277. // 支付成功
  278. if ($message['result_code'] === 'SUCCESS') {
  279. DB::beginTransaction();
  280. try {
  281. //增加销量,库存在拍下时已经减了
  282. IndustryProduct::query()
  283. ->where('id', $order->industry_product_id)
  284. ->increment('sale', $order->num);
  285. $old_status = $order->status;
  286. $pay_type = $order->pay_type;
  287. $money = $message['total_fee'] / 100;
  288. //定金支付和首付款支付
  289. if (in_array($pay_type, [PayType::DEPOSIT_PAY, PayType::EARNEST_PAY, PayType::DOWN_PAYMENT])) {
  290. if ($old_status == OrderStatus::UNPAID) {
  291. $order->status = OrderStatus::PAY_EARNEST;
  292. } else if ($old_status == OrderStatus::PAY_EARNEST) {
  293. $order->status = OrderStatus::PAID_RETAINAGE;
  294. $order->verify_code = uniqid(); //生成核销码
  295. }
  296. } else if ($pay_type == PayType::ONLINE) {
  297. $order->status = OrderStatus::PAID;
  298. $order->verify_code = uniqid(); //生成核销码
  299. }
  300. $order->paid_at = now();
  301. $order->paid_money = DB::raw('`paid_money` + ' . $money);
  302. $order->timeout = null;
  303. $order->save();
  304. //给供应商加钱
  305. $supplier = Supplier::find($order->supplier_id);
  306. $supplier->balance = DB::raw('`balance` + ' . $money);
  307. $supplier->save();
  308. //资金流水
  309. IndustryPayLog::create([
  310. 'agent_id' => $order->agent_id,
  311. 'supplier_id' => $order->supplier_id,
  312. 'money' => $money,
  313. 'industry_order_id' => $order->id,
  314. 'type' => 1,
  315. 'desc' => DB::raw("LEFT('购买产品:{$order->title}', 250)"),
  316. 'transaction_id' => $message['transaction_id'], //微信支付订单号
  317. 'created_at' => now(), //模型没有updated_at,无法自动写入时间
  318. 'out_trade_no' => $message['out_trade_no'] ?? '',
  319. ]);
  320. DB::commit();
  321. return true;
  322. } catch (Exception $e) {
  323. DB::rollBack();
  324. $fail('Unknown error');
  325. }
  326. } // 支付失败
  327. else if ($message['result_code'] === 'FAIL') {
  328. return true;
  329. }
  330. }
  331. // 希望微信重试
  332. $fail('Unknown error 2');
  333. });
  334. } catch (InvalidSignException | Exception | \Exception $e) {
  335. LOG::debug('行业产品支付:', [$e->getFile(), $e->getLine(), $e->getMessage()]);
  336. return 'error';
  337. }
  338. return $response;
  339. }
  340. /**
  341. * 商家入驻支付回调
  342. */
  343. public function SettledNotify()
  344. {
  345. try {
  346. $response = $this->app->handlePaidNotify(function ($message, $fail) {
  347. //仅测试用,回调记录
  348. DB::table('pay_debugs')->insert(['agent_id' => -2, 'type' => 1, 'content' => json_encode($message)]);
  349. // 请求成功
  350. if ($message['return_code'] === 'SUCCESS') {
  351. $order_no = $message['out_trade_no'];
  352. $order = SettledOrder::query()->where(['order_no' => $order_no])->first();
  353. //已经处理过的订单直接返回true
  354. if ($order && $order->status == 1) {
  355. return true;
  356. }
  357. // 支付成功
  358. if ($message['result_code'] === 'SUCCESS') {
  359. try {
  360. $order->transaction_id = $message['transaction_id'];
  361. $order->paid_money = $message['total_fee'] / 100;
  362. $order->paid_at = now();
  363. $order->status = 1;
  364. $order->save();
  365. return true;
  366. } catch (Exception $e) {
  367. $fail('Unknown error');
  368. }
  369. } // 支付失败
  370. else if ($message['result_code'] === 'FAIL') {
  371. return true;
  372. }
  373. }
  374. // 希望微信重试
  375. $fail('Unknown error 2');
  376. });
  377. } catch (Exception | \Exception $e) {
  378. LOG::debug('商家入驻支付回调:', [$e->getFile(), $e->getLine(), $e->getMessage()]);
  379. return 'error';
  380. }
  381. return $response;
  382. }
  383. }