Skip to content

Commit

Permalink
[14.x] New subscription behavior (#1420)
Browse files Browse the repository at this point in the history
* Change subscription flow

* wip

* wip

* wip

* wip
  • Loading branch information
driesvints authored Aug 19, 2022
1 parent f09422c commit 6b9b640
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 28 deletions.
59 changes: 59 additions & 0 deletions src/Concerns/HandlesPaymentFailures.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Laravel\Cashier\Concerns;

use Laravel\Cashier\Exceptions\IncompletePayment;
use Laravel\Cashier\Payment;
use Laravel\Cashier\Subscription;
use Stripe\Exception\CardException as StripeCardException;
use Stripe\PaymentMethod as StripePaymentMethod;

trait HandlesPaymentFailures
{
/**
* Handle a failed payment for the given subscription.
*
* @param \Laravel\Cashier\Subscription $subscription
* @param \Stripe\PaymentMethod|string|null $paymentMethod
* @return void
*
* @throws \Laravel\Cashier\Exceptions\IncompletePayment
*
* @internal
*/
public function handlePaymentFailure(Subscription $subscription, $paymentMethod = null)
{
if ($subscription->hasIncompletePayment()) {
try {
$subscription->latestPayment()->validate();
} catch (IncompletePayment $e) {
if ($e->payment->requiresConfirmation()) {
try {
if ($paymentMethod) {
$paymentIntent = $e->payment->confirm([
'expand' => ['invoice.subscription'],
'payment_method' => $paymentMethod instanceof StripePaymentMethod
? $paymentMethod->id
: $paymentMethod,
]);
} else {
$paymentIntent = $e->payment->confirm(['expand' => ['invoice.subscription']]);
}
} catch (StripeCardException) {
$paymentIntent = $e->payment->asStripePaymentIntent(['invoice.subscription']);
}

$subscription->fill([
'stripe_status' => $paymentIntent->invoice->subscription->status,
])->save();

if ($subscription->hasIncompletePayment()) {
(new Payment($paymentIntent))->validate();
}
} else {
throw $e;
}
}
}
}
}
14 changes: 13 additions & 1 deletion src/Concerns/InteractsWithPaymentBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,19 @@ trait InteractsWithPaymentBehavior
*
* @var string
*/
protected $paymentBehavior = StripeSubscription::PAYMENT_BEHAVIOR_ALLOW_INCOMPLETE;
protected $paymentBehavior = StripeSubscription::PAYMENT_BEHAVIOR_DEFAULT_INCOMPLETE;

/**
* Set any new subscription as incomplete when created.
*
* @return $this
*/
public function defaultIncomplete()
{
$this->paymentBehavior = StripeSubscription::PAYMENT_BEHAVIOR_DEFAULT_INCOMPLETE;

return $this;
}

/**
* Allow subscription changes even if payment fails.
Expand Down
25 changes: 14 additions & 11 deletions src/Subscription.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Illuminate\Support\Collection;
use InvalidArgumentException;
use Laravel\Cashier\Concerns\AllowsCoupons;
use Laravel\Cashier\Concerns\HandlesPaymentFailures;
use Laravel\Cashier\Concerns\InteractsWithPaymentBehavior;
use Laravel\Cashier\Concerns\Prorates;
use Laravel\Cashier\Database\Factories\SubscriptionFactory;
Expand All @@ -24,6 +25,7 @@
class Subscription extends Model
{
use AllowsCoupons;
use HandlesPaymentFailures;
use HasFactory;
use InteractsWithPaymentBehavior;
use Prorates;
Expand Down Expand Up @@ -526,11 +528,7 @@ public function updateQuantity($quantity, $price = null)
'quantity' => $stripeSubscription->quantity,
])->save();

if ($this->hasIncompletePayment()) {
(new Payment(
$stripeSubscription->latest_invoice->payment_intent
))->validate();
}
$this->handlePaymentFailure($this);

return $this;
}
Expand Down Expand Up @@ -678,6 +676,7 @@ public function extendTrial(CarbonInterface $date)
* @param array $options
* @return $this
*
* @throws \Laravel\Cashier\Exceptions\IncompletePayment
* @throws \Laravel\Cashier\Exceptions\SubscriptionUpdateFailure
*/
public function swap($prices, array $options = [])
Expand Down Expand Up @@ -722,11 +721,7 @@ public function swap($prices, array $options = [])

$this->unsetRelation('items');

if ($this->hasIncompletePayment()) {
(new Payment(
$stripeSubscription->latest_invoice->payment_intent
))->validate();
}
$this->handlePaymentFailure($this);

return $this;
}
Expand Down Expand Up @@ -876,13 +871,21 @@ public function addPrice($price, $quantity = 1, array $options = [])

$this->unsetRelation('items');

$stripeSubscription = $this->asStripeSubscription();

if ($this->hasSinglePrice()) {
$this->fill([
'stripe_price' => null,
'quantity' => null,
])->save();
]);
}

$this->fill([
'stripe_status' => $stripeSubscription->status,
])->save();

$this->handlePaymentFailure($this);

return $this;
}

Expand Down
8 changes: 3 additions & 5 deletions src/SubscriptionBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Illuminate\Support\Collection;
use InvalidArgumentException;
use Laravel\Cashier\Concerns\AllowsCoupons;
use Laravel\Cashier\Concerns\HandlesPaymentFailures;
use Laravel\Cashier\Concerns\HandlesTaxes;
use Laravel\Cashier\Concerns\InteractsWithPaymentBehavior;
use Laravel\Cashier\Concerns\Prorates;
Expand All @@ -17,6 +18,7 @@
class SubscriptionBuilder
{
use AllowsCoupons;
use HandlesPaymentFailures;
use HandlesTaxes;
use InteractsWithPaymentBehavior;
use Prorates;
Expand Down Expand Up @@ -258,11 +260,7 @@ public function create($paymentMethod = null, array $customerOptions = [], array

$subscription = $this->createSubscription($stripeSubscription);

if ($subscription->hasIncompletePayment()) {
(new Payment(
$stripeSubscription->latest_invoice->payment_intent
))->validate();
}
$this->handlePaymentFailure($subscription, $paymentMethod);

return $subscription;
}
Expand Down
29 changes: 18 additions & 11 deletions src/SubscriptionItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Laravel\Cashier\Concerns\HandlesPaymentFailures;
use Laravel\Cashier\Concerns\InteractsWithPaymentBehavior;
use Laravel\Cashier\Concerns\Prorates;
use Laravel\Cashier\Database\Factories\SubscriptionItemFactory;
Expand All @@ -15,6 +16,7 @@
*/
class SubscriptionItem extends Model
{
use HandlesPaymentFailures;
use HasFactory;
use InteractsWithPaymentBehavior;
use Prorates;
Expand Down Expand Up @@ -115,18 +117,19 @@ public function updateQuantity($quantity)
'quantity' => $stripeSubscriptionItem->quantity,
])->save();

if ($this->subscription->hasSinglePrice()) {
$stripeSubscription = $this->subscription->asStripeSubscription();
$stripeSubscription = $this->subscription->asStripeSubscription();

if ($this->subscription->hasSinglePrice()) {
$this->subscription->fill([
'stripe_status' => $stripeSubscription->status,
'quantity' => $stripeSubscriptionItem->quantity,
])->save();
]);
}

if ($this->subscription->hasIncompletePayment()) {
optional($this->subscription->latestPayment())->validate();
}
$this->subscription->fill([
'stripe_status' => $stripeSubscription->status,
])->save();

$this->handlePaymentFailure($this->subscription);

return $this;
}
Expand Down Expand Up @@ -162,16 +165,20 @@ public function swap($price, array $options = [])
'quantity' => $stripeSubscriptionItem->quantity,
])->save();

$stripeSubscription = $this->subscription->asStripeSubscription();

if ($this->subscription->hasSinglePrice()) {
$this->subscription->fill([
'stripe_price' => $price,
'quantity' => $stripeSubscriptionItem->quantity,
])->save();
]);
}

if ($this->subscription->hasIncompletePayment()) {
optional($this->subscription->latestPayment())->validate();
}
$this->subscription->fill([
'stripe_status' => $stripeSubscription->status,
])->save();

$this->handlePaymentFailure($this->subscription);

return $this;
}
Expand Down

0 comments on commit 6b9b640

Please sign in to comment.