Cashier (Stripe)
Laravel Cashier (Stripe)
Introduction
Laravel Cashier Stripe provides an expressive, fluent interface to Stripe’s subscription billing services. It handles almost all of the boilerplate subscription billing code you are dreading writing. In addition to basic subscription management, Cashier can handle coupons, swapping subscription, subscription “quantities”, cancellation grace periods, and even generate invoice PDFs.
Upgrading Cashier
When upgrading to a new version of Cashier, it’s important that you carefully review the upgrade guide.
To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 15 utilizes Stripe API version 2023-10-16. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements.
Installation
First, install the Cashier package for Stripe using the Composer package manager:
composer require laravel/cashier
composer require laravel/cashier
After installing the package, publish Cashier’s migrations using the vendor:publish Artisan command:
php artisan vendor:publish --tag="cashier-migrations"
php artisan vendor:publish --tag="cashier-migrations"
Then, migrate your database:
php artisan migrate
php artisan migrate
Cashier’s migrations will add several columns to your users table. They will also create a new subscriptions table to hold all of your customer’s subscriptions and a subscription_items table for subscriptions with multiple prices.
If you wish, you can also publish Cashier’s configuration file using the vendor:publish Artisan command:
php artisan vendor:publish --tag="cashier-config"
php artisan vendor:publish --tag="cashier-config"
Lastly, to ensure Cashier properly handles all Stripe events, remember to configure Cashier’s webhook handling.
Stripe recommends that any column used for storing Stripe identifiers should be case-sensitive. Therefore, you should ensure the column collation for the stripe_id column is set to utf8_bin when using MySQL. More information regarding this can be found in the Stripe documentation.
Configuration
Billable Model
Before using Cashier, add the Billable trait to your billable model definition. Typically, this will be the App\Models\User model. This trait provides various methods to allow you to perform common billing tasks, such as creating subscriptions, applying coupons, and updating payment method information:
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
Cashier assumes your billable model will be the App\Models\User class that ships with Laravel. If you wish to change this you may specify a different model via the useCustomerModel method. This method should typically be called in the boot method of your AppServiceProvider class:
use App\Models\Cashier\User;
use Laravel\Cashier\Cashier;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::useCustomerModel(User::class);
}
use App\Models\Cashier\User;
use Laravel\Cashier\Cashier;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::useCustomerModel(User::class);
}
If you’re using a model other than Laravel’s supplied App\Models\User model, you’ll need to publish and alter the Cashier migrations provided to match your alternative model’s table name.
API Keys
Next, you should configure your Stripe API keys in your application’s .env file. You can retrieve your Stripe API keys from the Stripe control panel:
STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret
STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret
STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret
STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret
You should ensure that the STRIPE_WEBHOOK_SECRET environment variable is defined in your application’s .env file, as this variable is used to ensure that incoming webhooks are actually from Stripe.
Currency Configuration
The default Cashier currency is United States Dollars (USD). You can change the default currency by setting the CASHIER_CURRENCY environment variable within your application’s .env file:
CASHIER_CURRENCY=eur
CASHIER_CURRENCY=eur
In addition to configuring Cashier’s currency, you may also specify a locale to be used when formatting money values for display on invoices. Internally, Cashier utilizes PHP’s NumberFormatter class to set the currency locale:
CASHIER_CURRENCY_LOCALE=nl_BE
CASHIER_CURRENCY_LOCALE=nl_BE
In order to use locales other than en, ensure the ext-intl PHP extension is installed and configured on your server.
Tax Configuration
Thanks to Stripe Tax, it’s possible to automatically calculate taxes for all invoices generated by Stripe. You can enable automatic tax calculation by invoking the calculateTaxes method in the boot method of your application’s App\Providers\AppServiceProvider class:
use Laravel\Cashier\Cashier;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::calculateTaxes();
}
use Laravel\Cashier\Cashier;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::calculateTaxes();
}
Once tax calculation has been enabled, any new subscriptions and any one-off invoices that are generated will receive automatic tax calculation.
For this feature to work properly, your customer’s billing details, such as the customer’s name, address, and tax ID, need to be synced to Stripe. You may use the customer data synchronization and Tax ID methods offered by Cashier to accomplish this.
Logging
Cashier allows you to specify the log channel to be used when logging fatal Stripe errors. You may specify the log channel by defining the CASHIER_LOGGER environment variable within your application’s .env file:
CASHIER_LOGGER=stack
CASHIER_LOGGER=stack
Exceptions that are generated by API calls to Stripe will be logged through your application’s default log channel.
Using Custom Models
You are free to extend the models used internally by Cashier by defining your own model and extending the corresponding Cashier model:
use Laravel\Cashier\Subscription as CashierSubscription;
class Subscription extends CashierSubscription
{
// ...
}
use Laravel\Cashier\Subscription as CashierSubscription;
class Subscription extends CashierSubscription
{
// ...
}
After defining your model, you may instruct Cashier to use your custom model via the Laravel\Cashier\Cashier class. Typically, you should inform Cashier about your custom models in the boot method of your application’s App\Providers\AppServiceProvider class:
use App\Models\Cashier\Subscription;
use App\Models\Cashier\SubscriptionItem;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::useSubscriptionModel(Subscription::class);
Cashier::useSubscriptionItemModel(SubscriptionItem::class);
}
use App\Models\Cashier\Subscription;
use App\Models\Cashier\SubscriptionItem;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Cashier::useSubscriptionModel(Subscription::class);
Cashier::useSubscriptionItemModel(SubscriptionItem::class);
}
Quickstart
Selling Products
Before utilizing Stripe Checkout, you should define Products with fixed prices in your Stripe dashboard. In addition, you should configure Cashier’s webhook handling.
Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and Stripe Checkout, you can easily build modern, robust payment integrations.
To charge customers for non-recurring, single-charge products, we’ll utilize Cashier to direct customers to Stripe Checkout, where they will provide their payment details and confirm their purchase. Once the payment has been made via Checkout, the customer will be redirected to a success URL of your choosing within your application:
use Illuminate\Http\Request;
Route::get('/checkout', function (Request $request) {
$stripePriceId = 'price_deluxe_album';
$quantity = 1;
return $request->user()->checkout([$stripePriceId => $quantity], [
'success_url' => route('checkout-success'),
'cancel_url' => route('checkout-cancel'),
]);
})->name('checkout');
Route::view('/checkout/success', 'checkout.success')->name('checkout-success');
Route::view('/checkout/cancel', 'checkout.cancel')->name('checkout-cancel');
use Illuminate\Http\Request;
Route::get('/checkout', function (Request $request) {
$stripePriceId = 'price_deluxe_album';
$quantity = 1;
return $request->user()->checkout([$stripePriceId => $quantity], [
'success_url' => route('checkout-success'),
'cancel_url' => route('checkout-cancel'),
]);
})->name('checkout');
Route::view('/checkout/success', 'checkout.success')->name('checkout-success');
Route::view('/checkout/cancel', 'checkout.cancel')->name('checkout-cancel');
As you can see in the example above, we will utilize Cashier’s provided checkout method to redirect the customer to Stripe Checkout for a given “price identifier”. When using Stripe, “prices” refer to defined prices for specific products.
If necessary, the checkout method will automatically create a customer in Stripe and connect that Stripe customer record to the corresponding user in your application’s database. After completing the checkout session, the customer will be redirected to a dedicated success or cancellation page where you can display an informational message to the customer.
Providing Meta Data to Stripe Checkout
When selling products, it’s common to keep track of completed orders and purchased products via Cart and Order models defined by your own application. When redirecting customers to Stripe Checkout to complete a purchase, you may need to provide an existing order identifier so that you can associate the completed purchase with the corresponding order when the customer is redirected back to your application.
To accomplish this, you may provide an array of metadata to the checkout method. Let’s imagine that a pending Order is created within our application when a user begins the checkout process. Remember, the Cart and Order models in this example are illustrative and not provided by Cashier. You are free to implement these concepts based on the needs of your own application:
use App\Models\Cart;
use App\Models\Order;
use Illuminate\Http\Request;
Route::get('/cart/{cart}/checkout', function (Request $request, Cart $cart) {
$order = Order::create([
'cart_id' => $cart->id,
'price_ids' => $cart->price_ids,
'status' => 'incomplete',
]);
return $request->user()->checkout($order->price_ids, [
'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => route('checkout-cancel'),
'metadata' => ['order_id' => $order->id],
]);
})->name('checkout');
use App\Models\Cart;
use App\Models\Order;
use Illuminate\Http\Request;
Route::get('/cart/{cart}/checkout', function (Request $request, Cart $cart) {
$order = Order::create([
'cart_id' => $cart->id,
'price_ids' => $cart->price_ids,
'status' => 'incomplete',
]);
return $request->user()->checkout($order->price_ids, [
'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => route('checkout-cancel'),
'metadata' => ['order_id' => $order->id],
]);
})->name('checkout');
As you can see in the example above, when a user begins the checkout process, we will provide all of the cart / order’s associated Stripe price identifiers to the checkout method. Of course, your application is responsible for associating these items with the “shopping cart” or order as a customer adds them. We also provide the order’s ID to the Stripe Checkout session via the metadata array. Finally, we have added the CHECKOUT_SESSION_ID template variable to the Checkout success route. When Stripe redirects customers back to your application, this template variable will automatically be populated with the Checkout session ID.
Next, let’s build the Checkout success route. This is the route that users will be redirected to after their purchase has been completed via Stripe Checkout. Within this route, we can retrieve the Stripe Checkout session ID and the associated Stripe Checkout instance in order to access our provided meta data and update our customer’s order accordingly:
use App\Models\Order;
use Illuminate\Http\Request;
use Laravel\Cashier\Cashier;
Route::get('/checkout/success', function (Request $request) {
$sessionId = $request->get('session_id');
if ($sessionId === null) {
return;
}
$session = Cashier::stripe()->checkout->sessions->retrieve($sessionId);
if ($session->payment_status !== 'paid') {
return;
}
$orderId = $session['metadata']['order_id'] ?? null;
$order = Order::findOrFail($orderId);
$order->update(['status' => 'completed']);
return view('checkout-success', ['order' => $order]);
})->name('checkout-success');
use App\Models\Order;
use Illuminate\Http\Request;
use Laravel\Cashier\Cashier;
Route::get('/checkout/success', function (Request $request) {
$sessionId = $request->get('session_id');
if ($sessionId === null) {
return;
}
$session = Cashier::stripe()->checkout->sessions->retrieve($sessionId);
if ($session->payment_status !== 'paid') {
return;
}
$orderId = $session['metadata']['order_id'] ?? null;
$order = Order::findOrFail($orderId);
$order->update(['status' => 'completed']);
return view('checkout-success', ['order' => $order]);
})->name('checkout-success');
Please refer to Stripe’s documentation for more information on the data contained by the Checkout session object.
Selling Subscriptions
Before utilizing Stripe Checkout, you should define Products with fixed prices in your Stripe dashboard. In addition, you should configure Cashier’s webhook handling.
Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and Stripe Checkout, you can easily build modern, robust payment integrations.
To learn how to sell subscriptions using Cashier and Stripe Checkout, let’s consider the simple scenario of a subscription service with a basic monthly (price_basic_monthly) and yearly (price_basic_yearly) plan. These two prices could be grouped under a “Basic” product (pro_basic) in our Stripe dashboard. In addition, our subscription service might offer an Expert plan as pro_expert.
First, let’s discover how a customer can subscribe to our services. Of course, you can imagine the customer might click a “subscribe” button for the Basic plan on our application’s pricing page. This button or link should direct the user to a Laravel route which creates the Stripe Checkout session for their chosen plan:
use Illuminate\Http\Request;
Route::get('/subscription-checkout', function (Request $request) {
return $request->user()
->newSubscription('default', 'price_basic_monthly')
->trialDays(5)
->allowPromotionCodes()
->checkout([
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});
use Illuminate\Http\Request;
Route::get('/subscription-checkout', function (Request $request) {
return $request->user()
->newSubscription('default', 'price_basic_monthly')
->trialDays(5)
->allowPromotionCodes()
->checkout([
'success_url' => route('your-success-route'),
'cancel_url' => route('your-cancel-route'),
]);
});
As you can see in the example above, we will redirect the customer to a Stripe Checkout session which will allow them to subscribe to our Basic plan. After a successful checkout or cancellation, the customer will be redirected back to the URL we provided to the checkout method. To know when their subscription has actually started (since some payment methods require a few seconds to process), we’ll also need to configure Cashier’s webhook handling.
Now that customers can start subscriptions, we need to restrict certain portions of our application so that only subscribed users can access them. Of course, we can always determine a user’s current subscription status via the subscribed method provided by Cashier’s Billable trait:
@if ($user->subscribed())
<p>You are subscribed.</p>
@endif
@if ($user->subscribed())
<p>You are subscribed.</p>
@endif
We can even easily determine if a user is subscribed to specific product or price:
@if ($user->subscribedToProduct('pro_basic'))
<p>You are subscribed to our Basic product.</p>
@endif
@if ($user->subscribedToPrice('price_basic_monthly'))
<p>You are subscribed to our monthly Basic plan.</p>
@endif
@if ($user->subscribedToProduct('pro_basic'))
<p>You are subscribed to our Basic product.</p>
@endif
@if ($user->subscribedToPrice('price_basic_monthly'))
<p>You are subscribed to our monthly Basic plan.</p>
@endif
Building a Subscribed Middleware
For convenience, you may wish to create a middleware which determines if the incoming request is from a subscribed user. Once this middleware has been defined, you may easily assign it to a route to prevent users that are not subscribed from accessing the route:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class Subscribed
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): Response
{
if (! $request->user()?->subscribed()) {
// Redirect user to billing page and ask them to subscribe...
return redirect('/billing');
}
return $next($request);
}
}
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class Subscribed
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): Response
{
if (! $request->user()?->subscribed()) {
// Redirect user to billing page and ask them to subscribe...
return redirect('/billing');
}
return $next($request);
}
}
Once the middleware has been defined, you may assign it to a route:
use App\Http\Middleware\Subscribed;
Route::get('/dashboard', function () {
// ...
})->middleware([Subscribed::class]);
use App\Http\Middleware\Subscribed;
Route::get('/dashboard', function () {
// ...
})->middleware([Subscribed::class]);
Allowing Customers to Manage Their Billing Plan
Of course, customers may want to change their subscription plan to another product or “tier”. The easiest way to allow this is by directing customers to Stripe’s Customer Billing Portal, which provides a hosted user interface that allows customers to download invoices, update their payment method, and change subscription plans.
First, define a link or button within your application that directs users to a Laravel route which we will utilize to initiate a Billing Portal session:
<a href="{{ route('billing') }}">
Billing
</a>
<a href="{{ route('billing') }}">
Billing
</a>
Next, let’s define the route that initiates a Stripe Customer Billing Portal session and redirects the user to the Portal. The redirectToBillingPortal method accepts the URL that users should be returned to when exiting the Portal:
use Illuminate\Http\Request;
Route::get('/billing', function (Request $request) {
return $request->user()->redirectToBillingPortal(route('dashboard'));
})->middleware(['auth'])->name('billing');
use Illuminate\Http\Request;
Route::get('/billing', function (Request $request) {
return $request->user()->redirectToBillingPortal(route('dashboard'));
})->middleware(['auth'])->name('billing');
As long as you have configured Cashier’s webhook handling, Cashier will automatically keep your application’s Cashier-related database tables in sync by inspecting the incoming webhooks from Stripe. So, for example, when a user cancels their subscription via Stripe’s Customer Billing Portal, Cashier will receive the corresponding webhook and mark the subscription as “canceled” in your application’s database.
Customers
Retrieving Customers
You can retrieve a customer by their Stripe ID using the Cashier::findBillable method. This method will return an instance of the billable model:
use Laravel\Cashier\Cashier;
$user = Cashier::findBillable($stripeId);
use Laravel\Cashier\Cashier;
$user = Cashier::findBillable($stripeId);
Creating Customers
Occasionally, you may wish to create a Stripe customer without beginning a subscription. You may accomplish this using the createAsStripeCustomer method:
$stripeCustomer = $user->createAsStripeCustomer();
$stripeCustomer = $user->createAsStripeCustomer();
Once the customer has been created in Stripe, you may begin a subscription at a later date. You may provide an optional $options array to pass in any additional customer creation parameters that are supported by the Stripe API:
$stripeCustomer = $user->createAsStripeCustomer($options);
$stripeCustomer = $user->createAsStripeCustomer($options);
You may use the asStripeCustomer method if you want to return the Stripe customer object for a billable model:
$stripeCustomer = $user->asStripeCustomer();
$stripeCustomer = $user->asStripeCustomer();
The createOrGetStripeCustomer method may be used if you would like to retrieve the Stripe customer object for a given billable model but are not sure whether the billable model is already a customer within Stripe. This method will create a new customer in Stripe if one does not already exist:
$stripeCustomer = $user->createOrGetStripeCustomer();
$stripeCustomer = $user->createOrGetStripeCustomer();
Updating Customers
Occasionally, you may wish to update the Stripe customer directly with additional information. You may accomplish this using the updateStripeCustomer method. This method accepts an array of customer update options supported by the Stripe API:
$stripeCustomer = $user->updateStripeCustomer($options);
$stripeCustomer = $user->updateStripeCustomer($options);
Balances
Stripe allows you to credit or debit a customer’s “balance”. Later, this balance will be credited or debited on new invoices. To check the customer’s total balance you may use the balance method that is available on your billable model. The balance method will return a formatted string representation of the balance in the customer’s currency:
$balance = $user->balance();
$balance = $user->balance();
To credit a customer’s balance, you may provide a value to the creditBalance method. If you wish, you may also provide a description:
$user->creditBalance(500, 'Premium customer top-up.');
$user->creditBalance(500, 'Premium customer top-up.');
Providing a value to the debitBalance method will debit the customer’s balance:
$user->debitBalance(300, 'Bad usage penalty.');
$user->debitBalance(300, 'Bad usage penalty.');
The applyBalance method will create new customer balance transactions for the customer. You may retrieve these transaction records using the balanceTransactions method, which may be useful in order to provide a log of credits and debits for the customer to review:
// Retrieve all transactions...
$transactions = $user->balanceTransactions();
foreach ($transactions as $transaction) {
// Transaction amount...
$amount = $transaction->amount(); // $2.31
// Retrieve the related invoice when available...
$invoice = $transaction->invoice();
}
// Retrieve all transactions...
$transactions = $user->balanceTransactions();
foreach ($transactions as $transaction) {
// Transaction amount...
$amount = $transaction->amount(); // $2.31
// Retrieve the related invoice when available...
$invoice = $transaction->invoice();
}
Tax IDs
Cashier offers an easy way to manage a customer’s tax IDs. For example, the taxIds method may be used to retrieve all of the tax IDs that are assigned to a customer as a collection:
$taxIds = $user->taxIds();
$taxIds = $user->taxIds();
You can also retrieve a specific tax ID for a customer by its identifier:
$taxId = $user->findTaxId('txi_belgium');
$taxId = $user->findTaxId('txi_belgium');
You may create a new Tax ID by providing a valid type and value to the createTaxId method:
$taxId = $user->createTaxId('eu_vat', 'BE0123456789');
$taxId = $user->createTaxId('eu_vat', 'BE0123456789');
The createTaxId method will immediately add the VAT ID to the customer’s account. Verification of VAT IDs is also done by Stripe; however, this is an asynchronous process. You can be notified of verification updates by subscribing to the customer.tax_id.updated webhook event and inspecting the VAT IDs verification parameter. For more information on handling webhooks, please consult the documentation on defining webhook handlers.
You may delete a tax ID using the deleteTaxId method:
$user->deleteTaxId('txi_belgium');
$user->deleteTaxId('txi_belgium');
Syncing Customer Data With Stripe
Typically, when your application’s users update their name, email address, or other information that is also stored by Stripe, you should inform Stripe of the updates. By doing so, Stripe’s copy of the information will be in sync with your application’s.
To automate this, you may define an event listener on your billable model that reacts to the model’s updated event. Then, within your event listener, you may invoke the syncStripeCustomerDetails method on the model:
use App\Models\User;
use function Illuminate\Events\queueable;
/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
static::updated(queueable(function (User $customer) {
if ($customer->hasStripeId()) {
$customer->syncStripeCustomerDetails();
}
}));
}
use App\Models\User;
use function Illuminate\Events\queueable;
/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
static::updated(queueable(function (User $customer) {
if ($customer->hasStripeId()) {
$customer->syncStripeCustomerDetails();
}
}));
}
Now, every time your customer model is updated, its information will be synced with Stripe. For convenience, Cashier will automatically sync your customer’s information with Stripe on the initial creation of the customer.
You may customize the columns used for syncing customer information to Stripe by overriding a variety of methods provided by Cashier. For example, you may override the stripeName method to customize the attribute that should be considered the customer’s “name” when Cashier syncs customer information to Stripe:
/**
* Get the customer name that should be synced to Stripe.
*/
public function stripeName(): string|null
{
return $this->company_name;
}
/**
* Get the customer name that should be synced to Stripe.
*/
public function stripeName(): string|null
{
return $this->company_name;
}
Similarly, you may override the stripeEmail, stripePhone, stripeAddress, and stripePreferredLocales methods. These methods will sync information to their corresponding customer parameters when updating the Stripe customer object. If you wish to take total control over the customer information sync process, you may override the syncStripeCustomerDetails method.
Billing Portal
Stripe offers an easy way to set up a billing portal so that your customer can manage their subscription, payment methods, and view their billing history. You can redirect your users to the billing portal by invoking the redirectToBillingPortal method on the billable model from a controller or route:
use Illuminate\Http\Request;
Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal();
});
use Illuminate\Http\Request;
Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal();
});
By default, when the user is finished managing their subscription, they will be able to return to the home route of your application via a link within the Stripe billing portal. You may provide a custom URL that the user should return to by passing the URL as an argument to the redirectToBillingPortal method:
use Illuminate\Http\Request;
Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal(route('billing'));
});
use Illuminate\Http\Request;
Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal(route('billing'));
});
If you would like to generate the URL to the billing portal without generating an HTTP redirect response, you may invoke the billingPortalUrl method:
$url = $request->user()->billingPortalUrl(route('billing'));
$url = $request->user()->billingPortalUrl(route('billing'));
Payment Methods
Storing Payment Methods
In order to create subscriptions or perform “one-off” charges with Stripe, you will need to store a payment method and retrieve its identifier from Stripe. The approach used to accomplish this differs based on whether you plan to use the payment method for subscriptions or single charges, so we will examine both below.
Payment Methods for Subscriptions
When storing a customer’s credit card information for future use by a subscription, the Stripe “Setup Intents” API must be used to securely gather the customer’s payment method details. A “Setup Intent” indicates to Stripe the intention to charge a customer’s payment method. Cashier’s Billable trait includes the createSetupIntent method to easily create a new Setup Intent. You should invoke this method from the route or controller that will render the form which gathers your customer’s payment method details:
return view('update-payment-method', [
'intent' => $user->createSetupIntent()
]);
return view('update-payment-method', [
'intent' => $user->createSetupIntent()
]);
After you have created the Setup Intent and passed it to the view, you should attach its secret to the element that will gather the payment method. For example, consider this “update payment method” form:
<input id="card-holder-name" type="text">
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button" data-secret="{{ $intent->client_secret }}">
Update Payment Method
</button>
<input id="card-holder-name" type="text">
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button" data-secret="{{ $intent->client_secret }}">
Update Payment Method
</button>
Next, the Stripe.js library may be used to attach a Stripe Element to the form and securely gather the customer’s payment details:
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('stripe-public-key');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
</script>
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('stripe-public-key');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
</script>
Next, the card can be verified and a secure “payment method identifier” can be retrieved from Stripe using Stripe’s confirmCardSetup method:
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
cardButton.addEventListener('click', async (e) => {
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: { name: cardHolderName.value }
}
}
);
if (error) {
// Display "error.message" to the user...
} else {
// The card has been verified successfully...
}
});
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
cardButton.addEventListener('click', async (e) => {
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: { name: cardHolderName.value }
}
}
);
if (error) {
// Display "error.message" to the user...
} else {
// The card has been verified successfully...
}
});
After the card has been verified by Stripe, you may pass the resulting setupIntent.payment_method identifier to your Laravel application, where it can be attached to the customer. The payment method can either be added as a new payment method or used to update the default payment method. You can also immediately use the payment method identifier to create a new subscription.
If you would like more information about Setup Intents and gathering customer payment details please review this overview provided by Stripe.
Payment Methods for Single Charges
Of course, when making a single charge against a customer’s payment method, we will only need to use a payment method identifier once. Due to Stripe limitations, you may not use the stored default payment method of a customer for single charges. You must allow the customer to enter their payment method details using the Stripe.js library. For example, consider the following form:
<input id="card-holder-name" type="text">
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button">
Process Payment
</button>
<input id="card-holder-name" type="text">
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button">
Process Payment
</button>
After defining such a form, the Stripe.js library may be used to attach a Stripe Element to the form and securely gather the customer’s payment details:
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('stripe-public-key');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
</script>
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('stripe-public-key');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
</script>
Next, the card can be verified and a secure “payment method identifier” can be retrieved from Stripe using Stripe’s createPaymentMethod method:
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
cardButton.addEventListener('click', async (e) => {
const { paymentMethod, error } = await stripe.createPaymentMethod(
'card', cardElement, {
billing_details: { name: cardHolderName.value }
}
);
if (error) {
// Display "error.message" to the user...
} else {
// The card has been verified successfully...
}
});
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
cardButton.addEventListener('click', async (e) => {
const { paymentMethod, error } = await stripe.createPaymentMethod(
'card', cardElement, {
billing_details: { name: cardHolderName.value }
}
);
if (error) {
// Display "error.message" to the user...
} else {
// The card has been verified successfully...
}
});
If the card is verified successfully, you may pass the paymentMethod.id to your Laravel application and process a single charge.
Retrieving Payment Methods
The paymentMethods method on the billable model instance returns a collection of Laravel\Cashier\PaymentMethod instances:
$paymentMethods = $user->paymentMethods();
$paymentMethods = $user->paymentMethods();
By default, this method will return payment methods of every type. To retrieve payment methods of a specific type, you may pass the type as an argument to the method:
$paymentMethods = $user->paymentMethods('sepa_debit');
$paymentMethods = $user->paymentMethods('sepa_debit');
To retrieve the customer’s default payment method, the defaultPaymentMethod method may be used:
$paymentMethod = $user->defaultPaymentMethod();
$paymentMethod = $user->defaultPaymentMethod();
You can retrieve a specific payment method that is attached to the billable model using the findPaymentMethod method:
$paymentMethod = $user->findPaymentMethod($paymentMethodId);
$paymentMethod = $user->findPaymentMethod($paymentMethodId);
Payment Method Presence
To determine if a billable model has a default payment method attached to their account, invoke the hasDefaultPaymentMethod method:
if ($user->hasDefaultPaymentMethod()) {
// ...
}
if ($user->hasDefaultPaymentMethod()) {
// ...
}
You may use the hasPaymentMethod method to determine if a billable model has at least one payment method attached to their account:
if ($user->hasPaymentMethod()) {
// ...
}
if ($user->hasPaymentMethod()) {
// ...
}
This method will determine if the billable model has any payment method at all. To determine if a payment method of a specific type exists for the model, you may pass the type as an argument to the method:
if ($user->hasPaymentMethod('sepa_debit')) {
// ...
}
if ($user->hasPaymentMethod('sepa_debit')) {
// ...
}
Updating the Default Payment Method
The updateDefaultPaymentMethod method may be used to update a customer’s default payment method information. This method accepts a Stripe payment method identifier and will assign the new payment method as the default billing payment method:
$user->updateDefaultPaymentMethod($paymentMethod);
$user->updateDefaultPaymentMethod($paymentMethod);
To sync your default payment method information with the customer’s default payment method information in Stripe, you may use the updateDefaultPaymentMethodFromStripe method:
$user->updateDefaultPaymentMethodFromStripe();
$user->updateDefaultPaymentMethodFromStripe();
The default payment method on a customer can only be used for invoicing and creating new subscriptions. Due to limitations imposed by Stripe, it may not be used for single charges.
Adding Payment Methods
To add a new payment method, you may call the addPaymentMethod method on the billable model, passing the payment method identifier:
$user->addPaymentMethod($paymentMethod);
$user->addPaymentMethod($paymentMethod);
To learn how to retrieve payment method identifiers please review the payment method storage documentation.
Deleting Payment Methods
To delete a payment method, you may call the delete method on the Laravel\Cashier\PaymentMethod instance you wish to delete:
$paymentMethod->delete();
$paymentMethod->delete();
The deletePaymentMethod method will delete a specific payment method from the billable model:
$user->deletePaymentMethod('pm_visa');
$user->deletePaymentMethod('pm_visa');
The deletePaymentMethods method will delete all of the payment method information for the billable model:
$user->deletePaymentMethods();
$user->deletePaymentMethods();
By default, this method will delete payment methods of every type. To delete payment methods of a specific type you can pass the type as an argument to the method:
$user->deletePaymentMethods('sepa_debit');
$user->deletePaymentMethods('sepa_debit');
If a user has an active subscription, your application should not allow them to delete their default payment method.
Subscriptions
Subscriptions provide a way to set up recurring payments for your customers. Stripe subscriptions managed by Cashier provide support for multiple subscription prices, subscription quantities, trials, and more.
(本文超出站点单文档体积上限,已按小节截断)
