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

Commit

Permalink
Added handler to prorate/not prorate swaps between plans
Browse files Browse the repository at this point in the history
  • Loading branch information
rennokki committed May 11, 2021
1 parent 58d9e63 commit cea7925
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 101 deletions.
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ Currently, only Inertia with Stripe are supported. For Paddle and/or Livewire, a

![example](example.png)

- [Jetstream Cashier Billing Portal](#jetstream-cashier-billing-portal)
- [🤝 Supporting](#-supporting)
- [🚀 Installation](#-installation)
- [Cashier](#cashier)
- [Scaffolding](#scaffolding)
- [Stripe Checkout](#stripe-checkout)
- [Defining Plans](#defining-plans)
- [🙌 Usage](#-usage)
- [Custom Billables](#custom-billables)
- [Modifying Checkout](#modifying-checkout)
- [Proration Between Swaps](#proration-between-swaps)
- [🐛 Testing](#-testing)
- [🤝 Contributing](#-contributing)
- [🔒 Security](#--security)
- [🎉 Credits](#-credits)

## 🤝 Supporting

Renoki Co. on GitHub aims on bringing a lot of open source projects and helpful projects to the world. Developing and maintaining projects everyday is a harsh work and tho, we love it.
Expand Down Expand Up @@ -99,7 +115,7 @@ 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
### 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`:

Expand All @@ -125,7 +141,7 @@ class CashierRegisterServiceProvider extends BaseServiceProvider
}
```

## Modifying Checkout
### Modifying Checkout

You can intercept Checkout options on the fly:

Expand Down Expand Up @@ -159,6 +175,29 @@ class CashierRegisterServiceProvider extends BaseServiceProvider
}
```

### Proration Between Swaps

By default, Billing Portal prorates the swap between plans. If you wish to not prorate the subscription between swaps, you can specify this in your service provider:

```php
use RenokiCo\BillingPortal\BillingPortal;

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

BillingPortal::dontProrateOnSwap();
}
}
```

## 🐛 Testing

``` bash
Expand Down
108 changes: 12 additions & 96 deletions src/BillingPortal.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,12 @@
namespace RenokiCo\BillingPortal;

use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use RenokiCo\CashierRegister\Plan;

class BillingPortal
{
/**
* The callback to sync quotas for an user.
*
* @var Closure|null
*/
protected static $syncQuotasResolver;
use Traits\ResolvesQuotas;
use Traits\ResolvesStripeCheckout;

/**
* The closure that will be called to retrieve
Expand All @@ -25,47 +19,30 @@ class BillingPortal
protected static $billable;

/**
* The closure that will be called to get
* the right Stripe Checkout parameters to call.
* Wether the proration should occur when swapping between plans.
*
* @var null|Closure
* @var bool
*/
protected static $stripeCheckoutOptions;
protected static $proratesOnSwap = true;

/**
* The closure that will be called to modify
* the Stripe Checkout flow.
* Don't prorate on swapping.
*
* @var null|Closure
*/
protected static $stripeCheckoutResolver;

/**
* Register a method that will run when the
* subscription updates, in order to sync the quotas.
*
* @param \Closure $callback
* @return void
*/
public static function resolveQuotasSync(Closure $callback)
public static function dontProrateOnSwap()
{
static::$syncQuotasResolver = $callback;
static::$proratesOnSwap = false;
}

/**
* Run the syncing quotas callback.
* Wether the proration should occur when swapping.
*
* @param \Illuminate\Database\Eloquent\Model $billable
* @param \Illuminate\Database\Eloquent\Model $subscription
* @return void
* @return bool
*/
public static function syncQuotas(Model $billable, Model $subscription)
public static function proratesOnSwap(): bool
{
if (static::$syncQuotasResolver) {
$callback = static::$syncQuotasResolver;

$callback($billable, $subscription);
}
return static::$proratesOnSwap;
}

/**
Expand Down Expand Up @@ -94,65 +71,4 @@ public static function getBillable(Request $request)
? $closure($request)
: $request->user();
}

/**
* Set the Stripe Checkout options computator.
*
* @param \Closure $callback
* @return void
*/
public static function resolveStripeCheckoutOptions(Closure $callback)
{
static::$stripeCheckoutOptions = $callback;
}

/**
* Calculate the options for Stripe Checkout for
* a specific Billable mode, plan and subscription name.
*
* @param \Illuminate\Http\Request $request
* @param mixed $billable
* @param \RenokiCo\CashierRegister\Plan $plan
* @param string $subscription
* @return array
*/
public static function getStripeCheckoutOptions(Request $request, $billable, Plan $plan, string $subscription): array
{
$closure = static::$stripeCheckoutOptions;

return $closure
? $closure($request, $billable, $plan, $subscription)
: [];
}

/**
* Set the Stripe Checkout interceptor.
*
* @param \Closure $callback
* @return void
*/
public static function resolveStripeCheckout(Closure $callback)
{
static::$stripeCheckoutResolver = $callback;
}

/**
* Mutate the Stripe checkout for
* a specific Billable mode, plan and subscription name.
*
* @param \Laravel\Cashier\SubscriptionBuilder $checkout
* @param \Illuminate\Http\Request $request
* @param mixed $billable
* @param \RenokiCo\CashierRegister\Plan $plan
* @param string $subscription
* @return \Laravel\Cashier\SubscriptionBuilder
*/
public static function mutateCheckout($checkout, Request $request, $billable, Plan $plan, string $subscription)
{
$closure = static::$stripeCheckoutResolver;

return $closure
? $closure($checkout, $request, $billable, $plan, $subscription)
: $checkout;
}
}
13 changes: 10 additions & 3 deletions src/Http/Controllers/Inertia/SubscriptionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,16 @@ public function swapPlan(Request $request, string $newPlanId)
if (! $billable->subscribed($subscription->name, $plan->getId())) {
$hasValidSubscription = $subscription && $subscription->valid();

$subscription = $hasValidSubscription
? $subscription->swap($newPlanId)
: $billable->newSubscription($request->subscription, $newPlanId)->create(optional($billable->defaultPaymentMethod())->id);
$subscription = value(function () use ($hasValidSubscription, $subscription, $newPlanId, $request, $billable) {
if ($hasValidSubscription) {
return BillingPortal::proratesOnSwap()
? $subscription->swap($newPlanId)
: $subscription->noProrate()->swap($newPlanId);
}

return $billable->newSubscription($request->subscription, $newPlanId)
->create(optional($billable->defaultPaymentMethod())->id);
});
}

BillingPortal::syncQuotas($billable, $subscription);
Expand Down
44 changes: 44 additions & 0 deletions src/Traits/ResolvesQuotas.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace RenokiCo\BillingPortal\Traits;

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

trait ResolvesQuotas
{
/**
* The callback to sync quotas for an user.
*
* @var Closure|null
*/
protected static $syncQuotasResolver;

/**
* Register a method that will run when the
* subscription updates, in order to sync the quotas.
*
* @param \Closure $callback
* @return void
*/
public static function resolveQuotasSync(Closure $callback)
{
static::$syncQuotasResolver = $callback;
}

/**
* Run the syncing quotas callback.
*
* @param \Illuminate\Database\Eloquent\Model $billable
* @param \Illuminate\Database\Eloquent\Model $subscription
* @return void
*/
public static function syncQuotas(Model $billable, Model $subscription)
{
if (static::$syncQuotasResolver) {
$callback = static::$syncQuotasResolver;

$callback($billable, $subscription);
}
}
}
87 changes: 87 additions & 0 deletions src/Traits/ResolvesStripeCheckout.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

namespace RenokiCo\BillingPortal\Traits;

use Closure;
use Illuminate\Http\Request;
use RenokiCo\CashierRegister\Plan;

trait ResolvesStripeCheckout
{
/**
* The closure that will be called to get
* the right Stripe Checkout parameters to call.
*
* @var null|Closure
*/
protected static $stripeCheckoutOptions;

/**
* The closure that will be called to modify
* the Stripe Checkout flow.
*
* @var null|Closure
*/
protected static $stripeCheckoutResolver;

/**
* Set the Stripe Checkout options computator.
*
* @param \Closure $callback
* @return void
*/
public static function resolveStripeCheckoutOptions(Closure $callback)
{
static::$stripeCheckoutOptions = $callback;
}

/**
* Calculate the options for Stripe Checkout for
* a specific Billable mode, plan and subscription name.
*
* @param \Illuminate\Http\Request $request
* @param mixed $billable
* @param \RenokiCo\CashierRegister\Plan $plan
* @param string $subscription
* @return array
*/
public static function getStripeCheckoutOptions(Request $request, $billable, Plan $plan, string $subscription): array
{
$closure = static::$stripeCheckoutOptions;

return $closure
? $closure($request, $billable, $plan, $subscription)
: [];
}

/**
* Set the Stripe Checkout interceptor.
*
* @param \Closure $callback
* @return void
*/
public static function resolveStripeCheckout(Closure $callback)
{
static::$stripeCheckoutResolver = $callback;
}

/**
* Mutate the Stripe checkout for
* a specific Billable mode, plan and subscription name.
*
* @param \Laravel\Cashier\SubscriptionBuilder $checkout
* @param \Illuminate\Http\Request $request
* @param mixed $billable
* @param \RenokiCo\CashierRegister\Plan $plan
* @param string $subscription
* @return \Laravel\Cashier\SubscriptionBuilder
*/
public static function mutateCheckout($checkout, Request $request, $billable, Plan $plan, string $subscription)
{
$closure = static::$stripeCheckoutResolver;

return $closure
? $closure($checkout, $request, $billable, $plan, $subscription)
: $checkout;
}
}

0 comments on commit cea7925

Please sign in to comment.