From 16f7f4cb99a8542a948a4badbc0f98844ef48369 Mon Sep 17 00:00:00 2001 From: IceToast Date: Thu, 16 Dec 2021 18:03:38 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20Added=20StripeWebhook=20Rou?= =?UTF-8?q?te?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 4 + .../Controllers/Admin/PaymentController.php | 152 +++++++++++++----- app/Http/Middleware/VerifyCsrfToken.php | 2 +- routes/web.php | 4 +- 4 files changed, 122 insertions(+), 40 deletions(-) diff --git a/.env.example b/.env.example index 2a4e63db..6ccbb538 100644 --- a/.env.example +++ b/.env.example @@ -27,6 +27,10 @@ PAYPAL_EMAIL= #stripe details, you only need "test" for testing! you can do this by setting the APP_ENV to local STRIPE_TEST_SECRET= STRIPE_SECRET= +#https://dashboard.stripe.com/webhooks +STRIPE_ENDPOINT_TEST_SECRET= +STRIPE_ENDPOINT_SECRET= + #stripe payment methods, comma seperated list of methods you want to support: #read into https://stripe.com/docs/payments/payment-methods/integration-options and/or https://stripe.com/docs/payments/payment-methods/overview STRIPE_METHODS= diff --git a/app/Http/Controllers/Admin/PaymentController.php b/app/Http/Controllers/Admin/PaymentController.php index 10bfe6c1..d085922a 100644 --- a/app/Http/Controllers/Admin/PaymentController.php +++ b/app/Http/Controllers/Admin/PaymentController.php @@ -17,6 +17,7 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Log; use PayPalCheckoutSdk\Core\PayPalHttpClient; use PayPalCheckoutSdk\Core\ProductionEnvironment; use PayPalCheckoutSdk\Core\SandboxEnvironment; @@ -285,48 +286,67 @@ class PaymentController extends Controller $stripeClient = $this->getStripeClient(); try{ - $paymentSession = $stripeClient->checkout->sessions->retrieve($request->input('session_id')); - $capturedPaymentIntent = $stripeClient->paymentIntents->capture($paymentSession->payment_intent); - if ($capturedPaymentIntent->status == "succeeded") { + //get stripe data + $paymentSession = $stripeClient->checkout->sessions->retrieve($request->input('session_id')); + $paymentIntent = $stripeClient->paymentIntents->retrieve($paymentSession->payment_intent); - //update credits - $user->increment('credits', $creditProduct->quantity); + //get DB entry of this payment ID if existing + $paymentDbEntry = Payment::where('payment_id', $paymentSession->payment_intent)->count(); - //update server limit - if (Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) { - if ($user->server_limit < Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')) { - $user->update(['server_limit' => Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')]); + // check if payment is 100% completed and payment does not exist in db already + if ($paymentSession->status == "complete" && $paymentIntent->status == "succeeded" && $paymentDbEntry == 0) { + + //update credits + $user->increment('credits', $creditProduct->quantity); + + //update server limit + if (Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) { + if ($user->server_limit < Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')) { + $user->update(['server_limit' => Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')]); + } + } + + //update role + if ($user->role == 'member') { + $user->update(['role' => 'client']); + } + + //store payment + $payment = Payment::create([ + 'user_id' => $user->id, + 'payment_id' => $paymentSession->payment_intent, + 'payment_method' => 'stripe', + 'type' => 'Credits', + 'status' => 'paid', + 'amount' => $creditProduct->quantity, + 'price' => $creditProduct->price, + 'tax_value' => $creditProduct->getTaxValue(), + 'total_price' => $creditProduct->getTotalPrice(), + 'tax_percent' => $creditProduct->getTaxPercent(), + 'currency_code' => $creditProduct->currency_code, + ]); + + //payment notification + $user->notify(new ConfirmPaymentNotification($payment)); + + event(new UserUpdateCreditsEvent($user)); + + //redirect back to home + return redirect()->route('home')->with('success', __('Your credit balance has been increased!')); + }else{ + if($paymentIntent->status != "processing"){ + //redirect back to home + return redirect()->route('home')->with('success', __('Your payment is being processed!')); + } + if($paymentDbEntry == 0){ + $stripeClient->paymentIntents->cancel($paymentIntent->id); + + //redirect back to home + return redirect()->route('home')->with('success', __('Your payment has been canceled!')); + }else{ + abort(402); } } - - //update role - if ($user->role == 'member') { - $user->update(['role' => 'client']); - } - - //store payment - $payment = Payment::create([ - 'user_id' => $user->id, - 'payment_id' => $capturedPaymentIntent->id, - 'payment_method' => 'stripe', - 'type' => 'Credits', - 'status' => 'paid', - 'amount' => $creditProduct->quantity, - 'price' => $creditProduct->price, - 'tax_value' => $creditProduct->getTaxValue(), - 'total_price' => $creditProduct->getTotalPrice(), - 'tax_percent' => $creditProduct->getTaxPercent(), - 'currency_code' => $creditProduct->currency_code, - ]); - - //payment notification - $user->notify(new ConfirmPaymentNotification($payment)); - - event(new UserUpdateCreditsEvent($user)); - - //redirect back to home - return redirect()->route('home')->with('success', __('Your credit balance has been increased!')); - } }catch (HttpException $ex) { if (env('APP_ENV') == 'local') { echo $ex->statusCode; @@ -337,6 +357,50 @@ class PaymentController extends Controller } } + /** + * @param Request $request + */ + public function StripeWebhooks(Request $request) + { + + \Stripe\Stripe::setApiKey($this->getStripeSecret()); + + + + try { + $payload = @file_get_contents('php://input'); + $sig_header = $request->header('Stripe-Signature'); + $event = null; + $event = \Stripe\Webhook::constructEvent( + $payload, $sig_header, $this->getStripeEndpointSecret() + ); + } catch(\UnexpectedValueException $e) { + // Invalid payload + + abort(400); + } catch(\Stripe\Exception\SignatureVerificationException $e) { + // Invalid signature + + abort(400); + + } + + // Handle the event + switch ($event->type) { + case 'payment_intent.succeeded': + $paymentIntent = $event->data->object; // contains a \Stripe\PaymentIntent + error_log($paymentIntent->status); + break; + case 'payment_method.attached': + $paymentMethod = $event->data->object; // contains a \Stripe\PaymentMethod + error_log($paymentMethod); + break; + // ... handle other event types + default: + echo 'Received unknown event type ' . $event->type; + } + } + /** * @return \Stripe\StripeClient */ @@ -355,6 +419,18 @@ class PaymentController extends Controller : env('STRIPE_SECRET'); } + /** + * @return string + */ + protected function getStripeEndpointSecret() + { + return env('APP_ENV') == 'local' + ? env('STRIPE_ENDPOINT_TEST_SECRET') + : env('STRIPE_ENDPOINT_SECRET'); + } + + + /** * @return JsonResponse|mixed diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 0c13b854..45cfc598 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -12,6 +12,6 @@ class VerifyCsrfToken extends Middleware * @var array */ protected $except = [ - // + 'payment/StripeWebhooks' ]; } diff --git a/routes/web.php b/routes/web.php index cb45ed22..1e582f32 100644 --- a/routes/web.php +++ b/routes/web.php @@ -40,6 +40,9 @@ Route::middleware('guest')->get('/', function () { Auth::routes(['verify' => true]); +# Stripe WebhookRoute -> validation in Route Handler +Route::post('payment/StripeWebhooks', [PaymentController::class, 'StripeWebhooks'])->name('payment.StripeWebhooks'); + Route::middleware(['auth', 'checkSuspended'])->group(function () { #resend verification email Route::get('/email/verification-notification', function (Request $request) { @@ -66,7 +69,6 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () { Route::get('payment/PaypalSuccess', [PaymentController::class, 'PaypalSuccess'])->name('payment.PaypalSuccess'); Route::get('payment/StripePay/{creditProduct}', [PaymentController::class, 'StripePay'])->name('payment.StripePay'); Route::get('payment/StripeSuccess', [PaymentController::class, 'StripeSuccess'])->name('payment.StripeSuccess'); - Route::get('payment/Cancel', [PaymentController::class, 'Cancel'])->name('payment.Cancel');