-
Notifications
You must be signed in to change notification settings - Fork 680
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Payment Intents for charges and subscriptions
These changes bring support for the new payment intents api to charges and subscriptions. As there are quite some breaking changes here let's go over the most prominent ones below: Any payment action will now throw an exception when a payment either fails or when the payment requires a secondary action in order to be completed. This goes for single charges, invoicing customers directly, subscribing to a new plan or swapping plans. Developers can catch these exceptions and decide for themselves how to handle these by either letting Stripe handle everything for them (to be set up in the Stripe dashboard) or use the custom built-in solution which will be added in the next commit. A new status column is introduced for subscriptions as well. Whenever an attempt is made to subscribe to a plan but a secondary payment action is required, the subscription will be put into a state of "incomplete" while payment confirmation is awaited. As soon as payment has been properly processed, a webhook will update the subscription's status to active. After these changes, webhooks will be a fundamental part of how Cashier works and they're now required in order to properly handle any payment confirmations and off-session updates to subscriptions & customers. The charge method now only accepts a payment method instead of a token. Developers will need to update their JS integration to retrieve a payment method id instead of a source token. These changes were done because this is now the recommended way by Stripe to work with payment methods. More info about that can be found here: https://stripe.com/docs/payments/payment-methods#transitioning In an upcoming update all card methods as well as the create method on the subscription builder will be updated as well. Closes #636
- Loading branch information
1 parent
c30353d
commit 0a6214a
Showing
13 changed files
with
500 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
namespace Laravel\Cashier\Exceptions; | ||
|
||
use Exception; | ||
use Throwable; | ||
use Laravel\Cashier\Payment; | ||
|
||
class IncompletePayment extends Exception | ||
{ | ||
/** | ||
* The Cashier Payment object. | ||
* | ||
* @var \Laravel\Cashier\Payment | ||
*/ | ||
public $payment; | ||
|
||
/** | ||
* Create a new IncompletePayment instance. | ||
* | ||
* @param \Laravel\Cashier\Payment $payment | ||
* @param string $message | ||
* @param int $code | ||
* @param \Throwable|null $previous | ||
* @return void | ||
*/ | ||
public function __construct(Payment $payment, $message = '', $code = 0, Throwable $previous = null) | ||
{ | ||
parent::__construct($message, $code, $previous); | ||
|
||
$this->payment = $payment; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
|
||
namespace Laravel\Cashier\Exceptions; | ||
|
||
use Laravel\Cashier\Payment; | ||
|
||
class PaymentActionRequired extends IncompletePayment | ||
{ | ||
/** | ||
* Create a new PaymentActionRequired instance. | ||
* | ||
* @param \Laravel\Cashier\Payment $payment | ||
* @return self | ||
*/ | ||
public static function incomplete(Payment $payment) | ||
{ | ||
return new self( | ||
$payment, | ||
'The payment attempt failed because it needs an extra action before it can be completed.' | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
|
||
namespace Laravel\Cashier\Exceptions; | ||
|
||
use Laravel\Cashier\Payment; | ||
|
||
class PaymentFailure extends IncompletePayment | ||
{ | ||
/** | ||
* Create a new PaymentFailure instance. | ||
* | ||
* @param \Laravel\Cashier\Payment $payment | ||
* @return self | ||
*/ | ||
public static function cardError(Payment $payment) | ||
{ | ||
return new self( | ||
$payment, | ||
'The payment attempt failed because there was a card error.' | ||
); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
<?php | ||
|
||
namespace Laravel\Cashier; | ||
|
||
use Laravel\Cashier\Exceptions\PaymentFailure; | ||
use Stripe\PaymentIntent as StripePaymentIntent; | ||
use Laravel\Cashier\Exceptions\PaymentActionRequired; | ||
|
||
class Payment | ||
{ | ||
/** | ||
* The Stripe PaymentIntent instance. | ||
* | ||
* @var \Stripe\PaymentIntent | ||
*/ | ||
protected $paymentIntent; | ||
|
||
/** | ||
* Create a new Payment instance. | ||
* | ||
* @param \Stripe\PaymentIntent $paymentIntent | ||
* @return void | ||
*/ | ||
public function __construct(StripePaymentIntent $paymentIntent) | ||
{ | ||
$this->paymentIntent = $paymentIntent; | ||
} | ||
|
||
/** | ||
* The Stripe PaymentIntent ID. | ||
* | ||
* @return string | ||
*/ | ||
public function id() | ||
{ | ||
return $this->paymentIntent->id; | ||
} | ||
|
||
/** | ||
* Get the total amount that will be paid. | ||
* | ||
* @return string | ||
*/ | ||
public function amount() | ||
{ | ||
return Cashier::formatAmount($this->rawAmount(), $this->paymentIntent->currency); | ||
} | ||
|
||
/** | ||
* Get the raw total amount that will be paid. | ||
* | ||
* @return int | ||
*/ | ||
public function rawAmount() | ||
{ | ||
return $this->paymentIntent->amount; | ||
} | ||
|
||
/** | ||
* The Stripe PaymentIntent client secret. | ||
* | ||
* @return string | ||
*/ | ||
public function clientSecret() | ||
{ | ||
return $this->paymentIntent->client_secret; | ||
} | ||
|
||
/** | ||
* Determine if the payment needs a valid payment method. | ||
* | ||
* @return bool | ||
*/ | ||
public function requiresPaymentMethod() | ||
{ | ||
return $this->paymentIntent->status === 'requires_payment_method'; | ||
} | ||
|
||
/** | ||
* Determine if the payment needs an extra action like 3D Secure. | ||
* | ||
* @return bool | ||
*/ | ||
public function requiresAction() | ||
{ | ||
return $this->paymentIntent->status === 'requires_action'; | ||
} | ||
|
||
/** | ||
* Determine if the payment was cancelled. | ||
* | ||
* @return bool | ||
*/ | ||
public function isCancelled() | ||
{ | ||
return $this->paymentIntent->status === 'cancelled'; | ||
} | ||
|
||
/** | ||
* Determine if the payment was successful. | ||
* | ||
* @return bool | ||
*/ | ||
public function isSucceeded() | ||
{ | ||
return $this->paymentIntent->status === 'succeeded'; | ||
} | ||
|
||
/** | ||
* Validate if the payment intent was successful and throw an exception if not. | ||
* | ||
* @return void | ||
* | ||
* @throws \Laravel\Cashier\Exceptions\PaymentActionRequired | ||
* @throws \Laravel\Cashier\Exceptions\PaymentFailure | ||
*/ | ||
public function validate() | ||
{ | ||
if ($this->requiresPaymentMethod()) { | ||
throw PaymentFailure::cardError($this); | ||
} elseif ($this->requiresAction()) { | ||
throw PaymentActionRequired::incomplete($this); | ||
} | ||
} | ||
|
||
/** | ||
* The Stripe PaymentIntent instance. | ||
* | ||
* @return \Stripe\PaymentIntent | ||
*/ | ||
public function asStripePaymentIntent() | ||
{ | ||
return $this->paymentIntent; | ||
} | ||
|
||
/** | ||
* Dynamically get values from the Stripe PaymentIntent. | ||
* | ||
* @param string $key | ||
* @return mixed | ||
*/ | ||
public function __get($key) | ||
{ | ||
return $this->paymentIntent->{$key}; | ||
} | ||
} |
Oops, something went wrong.