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

426 lines
13 KiB

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