Skip to content

Commit

Permalink
Support upgrading of user's plan through Stripe
Browse files Browse the repository at this point in the history
  • Loading branch information
range-of-motion committed Jul 28, 2020
1 parent 94db5ac commit 416443e
Show file tree
Hide file tree
Showing 14 changed files with 202 additions and 4 deletions.
4 changes: 4 additions & 0 deletions .env.example
Expand Up @@ -37,3 +37,7 @@ SUGGESTION_BOX_ENABLED=false
PLANS_ENABLED=false
PLANS_STANDARD_MAXIMUM_SPACES=2
PLANS_PREMIUM_MAXIMUM_SPACES=null

STRIPE_KEY=
STRIPE_SECRET=
STRIPE_PREMIUM_PLAN_PRICE_ID=
4 changes: 4 additions & 0 deletions .env.travis
Expand Up @@ -37,3 +37,7 @@ SUGGESTION_BOX_ENABLED=false
PLANS_ENABLED=true
PLANS_STANDARD_MAXIMUM_SPACES=2
PLANS_PREMIUM_MAXIMUM_SPACES=null

STRIPE_KEY=
STRIPE_SECRET=
STRIPE_PREMIUM_PLAN_PRICE_ID=
42 changes: 42 additions & 0 deletions app/Actions/CreateStripeCheckoutAction.php
@@ -0,0 +1,42 @@
<?php

namespace App\Actions;

use App\Exceptions\UserNotFoundException;
use App\Exceptions\UserStripelessException;
use App\Models\User;
use Stripe\StripeClient;

class CreateStripeCheckoutAction
{
public function execute(int $userId): string
{
$user = User::find($userId);

if (!$user) {
throw new UserNotFoundException();
}

if (!$user->stripe_customer_id) {
throw new UserStripelessException();
}

$stripe = new StripeClient(config('stripe.secret'));

$stripeCheckout = $stripe->checkout->sessions->create([
'success_url' => route('settings.billing'),
'cancel_url' => route('settings.billing'),
'mode' => 'subscription',
'payment_method_types' => ['card'],
'customer' => $user->stripe_customer_id,
'line_items' => [
[
'price' => config('stripe.premium_plan_price_id'),
'quantity' => 1
]
]
]);

return $stripeCheckout->id;
}
}
30 changes: 30 additions & 0 deletions app/Actions/CreateStripeCustomerAction.php
@@ -0,0 +1,30 @@
<?php

namespace App\Actions;

use App\Exceptions\UserNotFoundException;
use App\Models\User;
use Stripe\StripeClient;

class CreateStripeCustomerAction
{
public function execute(int $userId): void
{
$user = User::find($userId);

if (!$user) {
throw new UserNotFoundException();
}

$stripe = new StripeClient(config('stripe.secret'));

$customer = $stripe->customers->create([
'name' => $user->name,
'email' => $user->email
]);

$user->update([
'stripe_customer_id' => $customer->id
]);
}
}
10 changes: 10 additions & 0 deletions app/Exceptions/UserStripelessException.php
@@ -0,0 +1,10 @@
<?php

namespace App\Exceptions;

use Exception;

class UserStripelessException extends Exception
{
//
}
15 changes: 15 additions & 0 deletions app/Http/Controllers/SettingsController.php
Expand Up @@ -2,6 +2,8 @@

namespace App\Http\Controllers;

use App\Actions\CreateStripeCheckoutAction;
use App\Actions\CreateStripeCustomerAction;
use Illuminate\Http\Request;
use App\Mail\PasswordChanged;
use App\Models\Currency;
Expand Down Expand Up @@ -120,4 +122,17 @@ public function getBilling(Request $request)

return view('settings.billing', ['user' => $user]);
}

public function postUpgrade(Request $request)
{
$user = $request->user();

if (!$user->stripe_customer_id) {
(new CreateStripeCustomerAction())->execute($user->id);
}

$stripeCheckoutId = (new CreateStripeCheckoutAction())->execute($user->id);

return view('stripe_checkout_redirect', ['stripeCheckoutId' => $stripeCheckoutId]);
}
}
1 change: 1 addition & 0 deletions app/Models/User.php
Expand Up @@ -15,6 +15,7 @@ class User extends Authenticatable
'password',
'verification_token',
'last_verification_mail_sent_at',
'stripe_customer_id',
'plan'
];

Expand Down
1 change: 1 addition & 0 deletions composer.json
Expand Up @@ -15,6 +15,7 @@
"laravel/tinker": "^2.0",
"martinlindhe/laravel-vue-i18n-generator": "^0.1.39",
"nesbot/carbon": "^2.32",
"stripe/stripe-php": "^7.44",
"symfony/dom-crawler": "^5.1",
"symfony/process": "^5.0"
},
Expand Down
62 changes: 60 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions config/stripe.php
@@ -0,0 +1,7 @@
<?php

return [
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
'premium_plan_price_id' => env('STRIPE_PREMIUM_PLAN_PRICE_ID')
];
11 changes: 9 additions & 2 deletions resources/views/settings/billing.blade.php
Expand Up @@ -4,15 +4,22 @@
<h2 class="mb-3">{{ __('general.billing') }}</h2>
@endsection

@section('settings_body')
@section('settings_body_formless')
<div class="box">
<div class="box__section">
<div class="row" style="align-items: flex-start;">
<div class="mr-1">
<div style="color: black;" class="mb-1">{{ ucfirst($user->plan) }}</div>
€ 0.00
</div>
<a class="button button--small" href="#">Upgrade</a>
@if ($user->plan === 'standard')
<form method="POST" action="{{ route('settings.billing.upgrade') }}">
{{ csrf_field() }}
<button class="button button--small">Upgrade</button>
</form>
@else
<a class="button button--small button--secondary" href="#">Cancel</a>
@endif
</div>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions resources/views/settings/layout.blade.php
Expand Up @@ -23,6 +23,7 @@
{{ csrf_field() }}
@yield('settings_body')
</form>
@yield('settings_body_formless')
</div>
</div>
</div>
Expand Down
17 changes: 17 additions & 0 deletions resources/views/stripe_checkout_redirect.blade.php
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<body>
<script src="https://js.stripe.com/v3"></script>
<script>
var stripe = Stripe("{{ config('stripe.key') }}");
stripe.redirectToCheckout({
sessionId: '{{ $stripeCheckoutId }}'
}).then(function (result) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer
// using `result.error.message`.
});
</script>
</body>
</html>
1 change: 1 addition & 0 deletions routes/web.php
Expand Up @@ -122,6 +122,7 @@
Route::get('/settings/account', [SettingsController::class, 'getAccount'])->name('account');
Route::get('/settings/preferences', [SettingsController::class, 'getPreferences'])->name('preferences');
Route::get('/settings/billing', [SettingsController::class, 'getBilling'])->name('billing')->middleware('stripe');
Route::post('/settings/billing/upgrade', [SettingsController::class, 'postUpgrade'])->name('billing.upgrade')->middleware('stripe');
Route::get('/settings/spaces', [SettingsController::class, 'getSpaces'])->name('spaces.index');
});

Expand Down

0 comments on commit 416443e

Please sign in to comment.