Skip to content
This repository has been archived by the owner on Nov 9, 2021. It is now read-only.

Commit

Permalink
Merge pull request #3 from renoki-co/feature/team-support
Browse files Browse the repository at this point in the history
[feature] Custom billable support
  • Loading branch information
rennokki authored Jan 17, 2021
2 parents da635cd + f14512b commit 3b3e120
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 21 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,32 @@ By default, the subscriptions are accessible under `/user/billing/subscription`,

For more information about defining plans and quotas, check [Cashier Register documentation](https://github.com/renoki-co/cashier-register) and check [Laravel Cashier for Stripe documentation](https://laravel.com/docs/8.x/billing) on handling the billing.

## Custom Billables

By default, the billing is made directly on the currently authenticated model. In some cases like using billable trait on the Team model, you may change the model that will be retrieved from the current request. You may define it in the `boot()` method of `CashierRegisterServiceProvider`:

```php
use Illuminate\Http\Request;
use RenokiCo\BillingPortal\BillingPortal;

class CashierRegisterServiceProvider extends BaseServiceProvider
{
/**
* Boot the service provider.
*
* @return void
*/
public function boot()
{
parent::boot();

BillingPortal::setBillableOnRequest(function (Request $request) {
return $request->user()->currentTeam;
});
}
}
```

## 🐛 Testing

``` bash
Expand Down
36 changes: 36 additions & 0 deletions src/BillingPortal.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;

class BillingPortal
{
Expand All @@ -14,6 +15,14 @@ class BillingPortal
*/
protected static $syncQuotasCallback;

/**
* The closure that will be called to retrieve
* the billable model on a specific request.
*
* @var null|Closure
*/
protected static $billableOnRequest;

/**
* Register a method that will run when the
* subscription updates, in order to sync the quotas.
Expand Down Expand Up @@ -41,4 +50,31 @@ public static function syncQuotas(Model $user, Model $subscription)
$callback($user, $subscription);
}
}

/**
* Set the closure that returns the billable model
* by passing a specific request to it.
*
* @param Closure $callback
* @return void
*/
public static function setBillableOnRequest(Closure $callback)
{
static::$billableOnRequest = $callback;
}

/**
* Get the billable model from the request.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public static function getBillableFromRequest(Request $request)
{
$closure = static::$billableOnRequest;

return $closure
? $closure($request)
: $request->user();
}
}
3 changes: 2 additions & 1 deletion src/Http/Controllers/Inertia/BillingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Redirect;
use RenokiCo\BillingPortal\BillingPortal;

class BillingController extends Controller
{
Expand All @@ -18,7 +19,7 @@ class BillingController extends Controller
public function portal(Request $request)
{
return $this->getBillingPortalRedirect(
$request->user(), false
BillingPortal::getBillableFromRequest($request), false
);
}

Expand Down
3 changes: 2 additions & 1 deletion src/Http/Controllers/Inertia/InvoiceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Inertia\Inertia;
use RenokiCo\BillingPortal\BillingPortal;

class InvoiceController extends Controller
{
Expand All @@ -17,7 +18,7 @@ class InvoiceController extends Controller
*/
public function index(Request $request)
{
$invoices = $request->user()->invoices()->map(function ($invoice) {
$invoices = BillingPortal::getBillableFromRequest($request)->invoices()->map(function ($invoice) {
return [
'description' => $invoice->lines->data[0]->description,
'created' => Carbon::parse($invoice->created)->diffForHumans(),
Expand Down
21 changes: 11 additions & 10 deletions src/Http/Controllers/Inertia/PaymentMethodController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Illuminate\Routing\Controller;
use Inertia\Inertia;
use Redirect;
use RenokiCo\BillingPortal\BillingPortal;

class PaymentMethodController extends Controller
{
Expand All @@ -20,7 +21,7 @@ class PaymentMethodController extends Controller
public function __construct(Request $request)
{
$this->middleware(function (Request $request, Closure $next) {
$request->user()->createOrGetStripeCustomer();
BillingPortal::getBillableFromRequest($request)->createOrGetStripeCustomer();

return $next($request);
});
Expand All @@ -34,11 +35,11 @@ public function __construct(Request $request)
*/
public function index(Request $request)
{
$request->user()->updateDefaultPaymentMethodFromStripe();
BillingPortal::getBillableFromRequest($request)->updateDefaultPaymentMethodFromStripe();

$defaultPaymentMethod = $request->user()->defaultPaymentMethod();
$defaultPaymentMethod = BillingPortal::getBillableFromRequest($request)->defaultPaymentMethod();

$methods = $request->user()
$methods = BillingPortal::getBillableFromRequest($request)
->paymentMethods()
->filter(function ($method) {
return $method->type === 'card';
Expand Down Expand Up @@ -67,7 +68,7 @@ public function index(Request $request)
public function create(Request $request)
{
return Inertia::render('BillingPortal/PaymentMethod/Create', [
'intent' => $request->user()->createSetupIntent(),
'intent' => BillingPortal::getBillableFromRequest($request)->createSetupIntent(),
'stripe_key' => config('cashier.key'),
]);
}
Expand All @@ -84,10 +85,10 @@ public function store(Request $request)
'token' => ['required', 'string'],
]);

$request->user()->addPaymentMethod($request->token);
BillingPortal::getBillableFromRequest($request)->addPaymentMethod($request->token);

if (! $request->user()->hasDefaultPaymentMethod()) {
$request->user()->updateDefaultPaymentMethod($request->token);
if (! BillingPortal::getBillableFromRequest($request)->hasDefaultPaymentMethod()) {
BillingPortal::getBillableFromRequest($request)->updateDefaultPaymentMethod($request->token);
}

return Redirect::route('billing-portal.payment-method.index')
Expand All @@ -104,7 +105,7 @@ public function store(Request $request)
public function destroy(Request $request, string $paymentMethod)
{
try {
$paymentMethod = $request->user()->findPaymentMethod($paymentMethod);
$paymentMethod = BillingPortal::getBillableFromRequest($request)->findPaymentMethod($paymentMethod);
} catch (Exception $e) {
return Redirect::route('billing-portal.payment-method.index')
->with('success', 'The payment method got removed!');
Expand All @@ -128,7 +129,7 @@ public function destroy(Request $request, string $paymentMethod)
public function setDefault(Request $request, string $paymentMethod)
{
try {
$request->user()->updateDefaultPaymentMethod($paymentMethod);
BillingPortal::getBillableFromRequest($request)->updateDefaultPaymentMethod($paymentMethod);
} catch (Exception $e) {
return Redirect::route('billing-portal.payment-method.index')
->with('success', 'The default payment method got updated!');
Expand Down
18 changes: 9 additions & 9 deletions src/Http/Controllers/Inertia/SubscriptionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function __construct(Request $request)
*/
public function index(Request $request)
{
$user = $request->user();
$user = BillingPortal::getBillableFromRequest($request);

$subscription = $this->getCurrentSubscription($user, $request->subscription);

Expand All @@ -58,7 +58,7 @@ public function index(Request $request)
*/
public function subscribeToPlan(Request $request, string $planId)
{
$user = $request->user();
$user = BillingPortal::getBillableFromRequest($request);

$plan = Saas::getPlan($planId);

Expand All @@ -72,7 +72,7 @@ public function subscribeToPlan(Request $request, string $planId)

$subscription = $user->newSubscription($request->subscription, $planId)->create($payment);

$this->syncQuotas($request->user(), $subscription);
$this->syncQuotas(BillingPortal::getBillableFromRequest($request), $subscription);

return Redirect::route('billing-portal.subscription.index')
->with('success', "You have successfully subscribed to {$plan->getName()}!");
Expand All @@ -89,7 +89,7 @@ public function swapPlan(Request $request, string $newPlanId)
{
$plan = Saas::getPlan($newPlanId);

$user = $request->user();
$user = BillingPortal::getBillableFromRequest($request);

$subscription = $this->getCurrentSubscription($user, $request->subscription);

Expand All @@ -105,7 +105,7 @@ public function swapPlan(Request $request, string $newPlanId)
: $user->newSubscription($request->subscription, $newPlanId)->create(optional($user->defaultPaymentMethod())->id);
}

$this->syncQuotas($request->user(), $subscription);
$this->syncQuotas(BillingPortal::getBillableFromRequest($request), $subscription);

return Redirect::route('billing-portal.subscription.index')
->with('success', "The plan got successfully changed to {$plan->getName()}!");
Expand All @@ -119,15 +119,15 @@ public function swapPlan(Request $request, string $newPlanId)
*/
public function resumeSubscription(Request $request)
{
$user = $request->user();
$user = BillingPortal::getBillableFromRequest($request);

$subscription = $this->getCurrentSubscription($user, $request->subscription);

if ($subscription->onGracePeriod()) {
$subscription->resume();
}

$this->syncQuotas($request->user(), $subscription);
$this->syncQuotas(BillingPortal::getBillableFromRequest($request), $subscription);

return Redirect::route('billing-portal.subscription.index')
->with('success', 'The subscription has been resumed.');
Expand All @@ -141,15 +141,15 @@ public function resumeSubscription(Request $request)
*/
public function cancelSubscription(Request $request)
{
$user = $request->user();
$user = BillingPortal::getBillableFromRequest($request);

$subscription = $this->getCurrentSubscription($user, $request->subscription);

if ($subscription->recurring()) {
$subscription->cancel();
}

$this->syncQuotas($request->user(), $subscription);
$this->syncQuotas(BillingPortal::getBillableFromRequest($request), $subscription);

return Redirect::route('billing-portal.subscription.index')
->with('success', 'The current subscription got cancelled!');
Expand Down
6 changes: 6 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace RenokiCo\BillingPortal\Test;

use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Orchestra\Testbench\TestCase as Orchestra;
use RenokiCo\BillingPortal\BillingPortal;
use RenokiCo\CashierRegister\Saas;
use Stripe\ApiResource;
use Stripe\Exception\InvalidRequestException;
Expand Down Expand Up @@ -48,6 +50,10 @@ public function setUp(): void
Saas::feature('Build Minutes', 'build.minutes', 10),
Saas::feature('Seats', 'teams', 5)->notResettable(),
]);

BillingPortal::setBillableOnRequest(function (Request $request) {
return $request->user();
});
}

/**
Expand Down

0 comments on commit 3b3e120

Please sign in to comment.