the browser-facing portion of osu!
at master 4.6 kB view raw
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\Payments; 7 8use App\Exceptions\InvalidSignatureException; 9use App\Exceptions\Store\OrderException; 10use App\Exceptions\Store\PaymentRejectedException; 11use App\Libraries\OrderCheckout; 12use App\Libraries\Payments\NotificationType; 13use App\Libraries\Payments\PaypalCreatePayment; 14use App\Libraries\Payments\PaypalExecutePayment; 15use App\Libraries\Payments\PaypalPaymentProcessor; 16use App\Libraries\Payments\PaypalSignature; 17use App\Models\Store\Order; 18use App\Traits\CheckoutErrorSettable; 19use Illuminate\Database\QueryException; 20use Illuminate\Http\Request as HttpRequest; 21use Lang; 22use PayPalHttp\HttpException; 23 24class PaypalController extends Controller 25{ 26 use CheckoutErrorSettable; 27 28 public function __construct() 29 { 30 $this->middleware('auth', ['except' => ['ipn']]); 31 $this->middleware('check-user-restricted', ['except' => ['ipn']]); 32 $this->middleware('verify-user', ['except' => ['ipn']]); 33 34 parent::__construct(); 35 } 36 37 // When user has approved a payment at Paypal and is redirected back here. 38 public function approved() 39 { 40 // new uses token 41 $params = get_params(request()->all(), null, [ 42 'order_id:int', 43 'paymentId:string', 44 'token:string', 45 ], ['null_missing' => true]); 46 47 $order = auth()->user() 48 ->orders() 49 ->paymentRequested() 50 ->findOrFail($params['order_id']); 51 52 if (present($params['paymentId'])) { 53 return $this->setAndRedirectCheckoutError($order, osu_trans('paypal/errors.old_format')); 54 } 55 56 $token = $params['token']; 57 if (!present($token) || $token !== $order->reference) { 58 return $this->setAndRedirectCheckoutError($order, osu_trans('paypal/errors.invalid_token')); 59 } 60 61 try { 62 (new PaypalExecutePayment($order))->run(); 63 } catch (HttpException $e) { 64 return $this->setAndRedirectCheckoutError($order, $this->userErrorMessage($e)); 65 } catch (PaymentRejectedException) { 66 return $this->setAndRedirectCheckoutError($order, osu_trans('paypal/errors.unknown')); 67 } 68 69 return redirect(route('store.invoice.show', ['invoice' => $order->order_id, 'thanks' => 1])); 70 } 71 72 // Begin process of approving a payment. 73 public function create() 74 { 75 $orderId = get_int(request('order_id')); 76 77 $order = auth()->user()->orders()->paymentRequested()->findOrFail($orderId); 78 79 return (new PaypalCreatePayment($order))->run(); 80 } 81 82 // Payment declined by user. 83 public function declined() 84 { 85 $orderId = get_int(request('order_id')); 86 87 $order = auth()->user()->orders()->paymentRequested()->find($orderId); 88 89 if ($order === null) { 90 return ujs_redirect(route('store.cart.show')); 91 } 92 93 (new OrderCheckout($order, Order::PROVIDER_PAYPAL))->failCheckout(); 94 95 return $this->setAndRedirectCheckoutError($order, osu_trans('store.checkout.declined')); 96 } 97 98 // Called by Paypal. 99 public function ipn(HttpRequest $request) 100 { 101 $params = static::extractParams($request); 102 $signature = new PaypalSignature($request); 103 $processor = new PaypalPaymentProcessor($params, $signature); 104 105 try { 106 $processor->run(); 107 } catch (OrderException $exception) { 108 log_error($exception); 109 110 return response(['message' => 'A validation error occured while running the transaction'], 406); 111 } catch (InvalidSignatureException $exception) { 112 log_error($exception); 113 114 return response(['message' => $exception->getMessage()], 406); 115 } catch (QueryException $exception) { 116 // can get multiple cancellations for the same order from paypal. 117 if ( 118 is_sql_unique_exception($exception) 119 && $processor->getNotificationType() === NotificationType::REFUND 120 ) { 121 return 'ok'; 122 } 123 124 throw $exception; 125 } 126 127 return 'ok'; 128 } 129 130 private function userErrorMessage(HttpException $e) 131 { 132 $json = json_decode($e->getMessage()); 133 $key = 'paypal/errors.'.strtolower($json->name ?? 'unknown'); 134 if (!Lang::has($key)) { 135 $key = 'paypal/errors.unknown'; 136 } 137 138 return osu_trans($key); 139 } 140}