Charge Documentation
Last updated on December 24th, 2021
Overview
Charge is built on top of Cashier, and as such, the flow of how subscriptions work can be seen in its docs.
At the moment Charge only handles a subset of what Cashier can do. If you find something you need, please open an issue.
NOTE: Charge’s subscription tags require a logged in user.
Getting Started
Requirements
- PHP 7.4
- Laravel 7
- Stripe account
- Statamic users in the database
Installation
Grab from the marketplace or composer require silentz/charge
Configuration
Cashier
Ensure your users are stored in the database.
Add use Chargeable;
to your User.php
model like so:
use Illuminate\Foundation\Auth\User as Authenticatable;
use Silentz\Charge\Chargeable;
class User extends Authenticatable
{
use Notifiable;
use Chargeable;
...
Add the Stripe keys to your .env
file:
STRIPE_KEY=pk_test_key
STRIPE_SECRET=sk_test_key
STRIPE_WEBHOOK_SECRET=whsec_secret
If you want to override Cashier’s default configuration, publish its configuration with php artisan vendor:publish --tag=cashier
.
Run the Cashier migration, php artisan migrate
.
Set up the Stripe webhook, with https://yoursite.com/!/charge/webhook
as the endpoint.
Charge
Publish Charge’s config with php artisan vendor:publish --tag=charge-config
, it will be in config/charge.php
.
return [
/*
* When a user subscribes to a plan, which role should they be given?
*/
'plans_and_roles' => [
[
'plan' => 'the-stripe-plan',
'role' => 'the-role'
],
[
'plan' => 'another-stripe-plan',
'role' => 'another-role'
],
],
/*
* For all below, which template & subject to use for which email.
* Set to null to not send that email
*/
'emails' => [
// send email address for all Charge emails
'sender' => 'foo@bar.com',
'customer_updated' => [
'template' => 'emails.customer.updated',
'subject' => '',
],
'one_time_payment' => null,
'subscription_canceled' => [
'template' => '',
'subject' => '',
],
'subscription_created' => [
'template' => 'emails.subscription.created',
'subject' => 'Hello there',
],
'subscription_payment_failed' => [
'template' => '',
'subject' => '',
],
'subscription_payment_succeeded' => [
'template' => '',
'subject' => '',
],
'subscription_upcoming' => [
'template' => '',
'subject' => '',
],
'subscription_updated' => [
'template' => '',
'subject' => '',
],
],
];
General Flow
It’s important to understand how Stripe Subscriptions work, so please do read up.
Note that sometimes when you create a subscription, further action is required. When that happens, Charge will return you to a page of your choosing (see below), and set a couple of session variables, requires_action
& payment_intent
, so you can get the user to confirm the payment. See below for how to do that.
Usage
Create the Setup Intent
{{ subscription:setup_intent }}
returns the setup intent needed to create a payment method (see below).
Create the Subscription
Please see the Cashier docs for an explanation of how to create a subscription with Stripe Elements and Javascript.
The basics are, you need to create a payment method and pass that to Charge, along with the subscription details.
Creating a payment method requires the payment details (collected via Stripe Elements) and a Setup Intent (via {{ subscription:setup_intent }}
)
Parameters
-
redirect
- optional, go to this page when the subscription is created. -
error_redirect
- optional, go to this page if there was an error creating the subscription -
action_redirect
- optional, go to this page if there are actions that need completing (see below)
Any other attributes passed in will be added to the HTML form tag.
Variables
-
errors
- if there were errors creating a subscription, this is where you’ll find them. -
success
- if the subscription was created successfully, this will betrue
. -
subscription
- available if successful and contains all the Stripe subscription data. -
needs_action
- occasionally, Stripe will require folks to confirm the purchase. In that instance, this will betrue
and you should . -
action
- this is the action needed, currently onlyincomplete
is available. -
payment_method
- if action is needed, this is the payment method data, needed to confirm the payment.
List a User's Subscriptions
To get a list of a user’s subscriptions, use the {{ subscriptions }}
tag:
{{ subscriptions }}
{{ name }}
{{ stripe_id }}
{{ stripe_status }}
{{ stripe_plan }}
{{ quantity }}
{{ trial_ends_at }}
{{ ends_at }}
{{ /subscriptions }}
Please see the Cashier docs for an explanation on those fields.
Cancel a Subscription
Every subscription has an id
, to cancel, pass it to the {{ subscription:cancel }}
tag.
{{ subscription:cancel :id="id" }}
<button>Cancel</button>
{{ /subscription:cancel }}
To get the id
, loop through the user’s subscriptions via the {{ subscriptions }}
tag.
Parameters
-
cancel_immediately
- optional, set totrue
to cancel this subscription immediately, instead of at the end of the subscription. Defaults tofalse
.
Subscription Requires Action
Like mentioned above, sometimes a payment requires action. To check use the {{ subscription:requires_action }}
tag. It will return true if you need to get the user to confirm the payment (see below).
Payment Intent
If the payment does require confirmation, you need pass the payment intent’s secret into the confirmCardPayment
Stripe method.
Variables
All fields from Stripe’s PaymentIntent
are available to you, but you’ll likely only need the client_secret
.
Templating
Creating a Subscription
Use the subscription:create
tag to have the user subscribe to a plan. Don’t forget to pass the name
and the plan
, along with any other relevant information. Make sure the JS code has access to the setup intent.
Antlers
{{ subscription:create redirect="/success" id="the-form" }}
<input type="text" name="plan">
{{ if errors }}
Errors:
<ul>
{{ errors }}
<li>{{ value }}</li>
{{ /errors }}
</ul>
{{ /if }}
{{ if success }}
{{# show whatever you''d like when someone has successfully subscribed #}}!
{{ /if }}
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button" data-secret="{{ subscription:setup_intent }}">
Subscribe
</button>
{{ /subscription:create }}
Javascript
This is taken directly from the Cashier docs:
const stripe = Stripe('{{ config:cashier:key }}');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
const form = document.getElementById('the-form');
cardButton.addEventListener('click', async (e) => {
e.preventDefault();
cardButton.disabled= true;
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: {
name: '{{ first_name }} {{ last_name }}',
email: {{ email }}
}
}
}
);
if (error) {
alert(error);// Display "error.message" to the user...
} else {
// The card has been verified successfully...
addToForm('payment_method', setupIntent.payment_method, form);
form.submit();
}
});
function addToForm(name, value, form) {
let input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = value;
form.appendChild(input);
}
Confirm a Subscription
After the subscription is created, don’t forget to check {{ subscription:requires_action }}
and if true
, have the user confirm the payment.
Antlers
<h2>Confirm Subscription</h2>
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button" data-secret="{{ subscription:payment_intent }}{{ client_secret }}{{ /subscription:payment_intent }}">
Confirm
</button>
Javascript
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
cardButton.addEventListener('click', (e) => {
e.preventDefault();
cardButton.disabled = true;
stripe.confirmCardPayment(
clientSecret,
{
payment_method: {
card: cardElement,
billing_details: { name: {{ first_name }} {{ last_name }} }
}
}
).then(function (result) {
if (result.error) {
cardButton.disabled = true;
alert(result.error.code);
} else {
cardButton.disabled = true;
alert('success');
}
});
});