1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Http\Controllers\Store;
7
8use App\Libraries\OrderCheckout;
9use App\Libraries\Payments\PaymentCompleted;
10use App\Models\Store\Order;
11use App\Traits\CheckoutErrorSettable;
12use Auth;
13use DB;
14
15class CheckoutController extends Controller
16{
17 use CheckoutErrorSettable;
18
19 protected $layout = 'master';
20
21 public function __construct()
22 {
23 $this->middleware('auth');
24 if (!$this->isAllowRestrictedUsers()) {
25 $this->middleware('check-user-restricted');
26 }
27 $this->middleware('verify-user');
28
29 parent::__construct();
30 }
31
32 public function show($id)
33 {
34 $order = $this->orderForCheckout($id);
35 if ($order === null || $order->isEmpty() || $order->isShouldShopify()) {
36 return ujs_redirect(route('store.cart.show'));
37 }
38
39 // TODO: should be able to notify user that items were changed due to stock/price changes.
40 $order->refreshCost();
41 $checkout = new OrderCheckout($order);
42
43 // using $errors will conflict with laravel's default magic MessageBag/ViewErrorBag that doesn't act like
44 // an array and will cause issues in shared views.
45 $validationErrors = session('checkout.error.errors') ?? $checkout->validate();
46
47 return ext_view('store.checkout.show', compact('order', 'checkout', 'validationErrors'));
48 }
49
50 public function store()
51 {
52 $params = get_params(request()->all(), null, [
53 'hide_from_activity:bool',
54 'orderId:int',
55 'provider',
56 'shopifyCheckoutId',
57 ], ['null_missing' => true]);
58
59 $order = $this->orderForCheckout($params['orderId']);
60
61 if ($order === null || $order->isEmpty()) {
62 return ujs_redirect(route('store.cart.show'));
63 }
64
65 if ($params['hide_from_activity'] !== null) {
66 $order->setGiftsHidden($params['hide_from_activity']);
67 }
68
69 $checkout = new OrderCheckout($order, $params['provider'], $params['shopifyCheckoutId']);
70
71 $validationErrors = $checkout->validate();
72 if (!empty($validationErrors)) {
73 return $this->setAndRedirectCheckoutError(
74 $order,
75 osu_trans('store.checkout.cart_problems'),
76 $validationErrors
77 );
78 }
79
80 $checkout->beginCheckout();
81
82 if ((float) $order->getTotal() === 0.0) {
83 return $this->freeCheckout($checkout);
84 }
85
86 return 'ok';
87 }
88
89 private function freeCheckout($checkout)
90 {
91 $order = DB::connection('mysql-store')->transaction(function () use ($checkout) {
92 $order = $checkout->getOrder();
93 $checkout->completeCheckout();
94
95 (new PaymentCompleted($order, null))->handle();
96
97 return $order;
98 });
99
100 return ujs_redirect(route('store.invoice.show', ['invoice' => $order->order_id, 'thanks' => 1]));
101 }
102
103 private function orderForCheckout($id): ?Order
104 {
105 return Auth::user()
106 ->orders()
107 ->whereCanCheckout()
108 ->with('items.product')
109 ->find($id);
110 }
111}