- Charge requires PHP 7.1+
- Copy Charge folder to
site/addons - Run
php please update:addons - If you're going to use subscriptions, register the webhook w/ Stripe here. Set it to
https://yoursite.com/!/Charge/webhook.
-
Go to
cp/addons/charge/settingsand fill in the required fields.Collectionsis for if you are using Workshop. -
in
charge.yamlcharge_formsets- an array of formsets you'll be using for the Form submissioncharge_collections- which collection(s) you'll be using for the Workshop submissioncurrency- default currency for paymentsplan&role- when a customer signs for a plan, which role(s) should they havefrom_email- when the payment failed emails go out, what email account do they come fromcanceled_email_template&payment_failed_email_template- email templates to use for the failed payment emailsupcoming_payment_email_template- email template to use for the customer's upcoming payment/invoice
-
in your
.envfile, which MUST NOT be checked in:- please note the proper format for the key/value pair
STRIPE_SECRET_KEY- Stripe secret key, found here: https://dashboard.stripe.com/account/apikeysSTRIPE_PUBLIC_KEY- Stripe public key, found here: https://dashboard.stripe.com/account/apikeysSTRIPE_ENDPOINT_SECRET- Webhook signing secret, found here: https://dashboard.stripe.com/webhooks (click on the Charge webhook then "Click to reveal")- NOTE: not needed if you didn't register your webhook, i.e. are not using subscriptions
charge_formset: charge
charge_collections:
- things
currency: usd
plans_and_roles:
-
plan: associate
role:
- dd062758-f56c-4ca5-a381-fe88f9c54517
from_email: renewals@thedalzells.org
canceled_email_template: email/cancel_subscription
payment_failed_email_template: email/payment_failed
upcoming_payment_email_template: email/payment_upcoming
This is sent according to your Stripe settings (link to stripe docs), and assumes you have the webhook set up properly
In the email template, you have access to:
{{ plan }}- The short name of the subscription plan{{ first_name }}- The first name of the customer{{ last_name }}- The last name of the customer{{ due_date }}- When the payment will occur, in Unix timestamp format{{ amount }}- Amount that will be charged, in cents{{ currency }}- currency of payment
This is sent if a payment fails.
In the email template, you have access to:
{{ plan }}- The short name of the subscription plan{{ first_name }}- The first name of the customer{{ last_name }}- The last name of the customer{{ due_date }}- When the payment will occur, in Unix timestamp format{{ amount }}- Amount that will be charged, in cents{{ currency }}- currency of payment{{ attempt_count }}- how many times Charge has tried to process the payment{{ next_payment_attempt }}- when Charge will try again, in Unix timestamp format
NOTE: all ways below require {{ charge:js }} be loaded on the appropriate template. Stripe recommends that its library is loaded on every page for fraud detection so I suggest putting it in your layout.
A Stripe Customer is created on a charge, unless the customer has been charged before (via Charge).
For all options below, the charge details are available in the {{ charge:details }} tag.
There are four ways to use it:
- Statamic's
Formtag - Charge's
{{ charge:form }}- for when you want to use Stripe Checkout, etc - User registration form (for paid memberships, both subscriptions and one-time)
- Workshop entry creation
NOTE: if the user is logged in, the subscription details will be stored in the user data
Charge Form, {{ charge:payment_form }}
- for a one-time charge pass in the
amount(in cents), oramount_dollar(in decimal),description, and optionally thecurrencyas parameters on the tag - for a subscription, have a
planfield in your form with the Stripe Plan- if you want to discount the subscription, send a
couponvalue. See Stripe's documentation for setting up discounts.
- if you want to discount the subscription, send a
- if you want to redirect the customer after the charge, use a
redirectparameter - inside the tag,
success,errorsanddetailsare available as variables - outside the tag use
{{ charge:success }},{{ charge:errors }}and{{ charge:details }}instead.
Statamic Form, {{ form:create }}
- note the
attr="data-charge-form"in the form tag. - the following fields must be in your form:
stripeEmailoremail- email of customer
- for a one-time charge, somewhere in your form you need to set the
description,amount(in cents) oramount_dollars(like 23.45), and optionallycurrencyvia{{ charge:data }}or a form field - for a subscription, include a
planfield along with the above email field. Neithercurrency,amountnordescriptionare needed for subscriptions- if you want to discount the subscription, send a
couponvalue. See Stripe's documentation for setting up discounts.
- if you want to discount the subscription, send a
- the
customer_idis available in thesubmissiondata - please note the
data-*attributes on the form items. Those are required. - use the standard
successanderrorvariables.
Example - Charge Form - Stripe Checkout:
{{# currency is optional #}}
{{ charge:payment_form redirect="/thanks" amount="{amount}" description="{description}" currency="usd" }}
{{ if success }}
{{ details }}
ID: {{ id }}
{{ /details }}
{{ /if}}
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ env:STRIPE_PUBLIC_KEY }}"
data-amount="{{ amount }}"
data-name="{{ company }}"
data-description="{{ description }}"
data-image="/img/documentation/checkout/marketplace.png"
data-locale="auto"
data-currency="{{ currency }}">
</script>
{{ /charge:payment_form }}
If you're customizing Stripe Checkout, make sure to add the token to the form:
<script>
var handler = StripeCheckout.configure({
key: '{{ env:STRIPE_PUBLIC_KEY }}',
image: 'https://stripe.com/img/documentation/checkout/marketplace.png',
locale: 'auto',
token: function (token) {
let form = document.querySelector('#foo');
// You can access the token ID with `token.id`.
addTokenToForm(token, form);
// Get the token ID to your server-side code for use.
form.submit();
}
});
document.getElementById('customButton-6').addEventListener('click', function (e) {
// Open Checkout with further options:
handler.open({
name: 'Charge - Stripe Addon',
description: '{{ title }}',
currency: 'eur',
amount: 1000
});
e.preventDefault();
});
// Close Checkout on page navigation:
window.addEventListener('popstate', function () {
handler.close();
});
</script>
Example - Statamic Form:
{{ form:create in="charge" attr="class:form|data-charge-form" redirect="/thanks" }}
<div class="form-item">
<label>Email</label>
<input type="email" name="email" value="{{ old:email }}" />
</div>
<fieldset class="payment">
<div class="form-item">
<label for="cc_name">Name on Card</label>
<input type="text" data-stripe="name" id="cc_name" required>
</div>
<div class="form-item">
<label for="address_zip">Zip/Postal Code</label>
<input type="text" data-stripe="address_zip" id="address_zip" required>
</div>
<div class="form-item">
<label for="cc_number">Card Number</label>
<input type="text" data-stripe="number" id="cc_number" required>
</div>
<div class="row row-inner">
<div class="col">
<div class="form-item">
<label for="exp_month">Expiry Month</label>
<input type="text" data-stripe="exp_month" id="exp_month" maxlength="2" required placeholder="00">
</div>
</div>
<div class="col">
<div class="form-item">
<label for="exp_year">Expiry Year</label>
<input type="text" data-stripe="exp_year" id="exp_year" maxlength="2" required placeholder="00">
</div>
</div>
<div class="col">
<div class="form-item">
<label for="cvc">CVC</label>
<input type="text" data-stripe="cvc" id="cvc" maxlength="4" required placeholder="0000">
</div>
</div>
</div>
</fieldset>
{{ charge:data :amount="amount" :description="description" currency="cad" }}
<button class="button primary" id="register" data-charge-button>Pay</button>
{{ /form:create }}
For a subscription, like above, but no {{ charge:data }} needed, instead:
<div class="form-item">
<label for="plan">Membership Type</label>
<select name="plan" id="plan" class="big" >
<option>Please Select</option>
<option value="associate">Associate</option>
<option value="clinical">Clinical</option>
<option value="student">Student</option>
</select>
</div>
For a membership upon user registration:
<section class="regular">
<header>
<h1>Register</h1>
</header>
<article class="content">
{{ user:register_form redirect="/account" attr="class:form|data-charge-form" }}
{{ if errors }}
<div class="alert alert-danger">
{{ errors }}
{{ value }}<br>
{{ /errors }}
</div>
{{ /if }}
<div class="row row-inner">
<div class="col">
<div class="form-item">
<label>Email</label>
<input type="text" name="email" value="{{ old:email }}" />
</div>
</div>
<div class="col">
<div class="form-item">
<label>First Name</label>
<input type="text" name="first_name" value="{{ old:first_name }}" />
</div>
</div>
<div class="col">
<div class="form-item">
<label>Last Name</label>
<input type="text" name="last_name" value="{{ old:last_name }}" />
</div>
</div>
</div>
<div class="form-item">
<label>Password</label>
<input type="password" name="password" />
</div>
<div class="form-item">
<label>Password Confirmation</label>
<input type="password" name="password_confirmation" />
</div>
<fieldset class="payment">
<legend>Payment</legend>
<div class="form-item">
<label for="plan">Membership Type</label>
<select name="plan" id="plan" class="big" >
<option>Please Select</option>
<option value="associate">Associate</option>
<option value="clinical">Clinical</option>
<option value="student">Student</option>
</select>
</div>
<div class="form-item">
<label for="cc_name">Name on Card</label>
<input type="text" data-stripe="name" id="cc_name" required>
</div>
<div class="form-item">
<label for="cc_number">Zip/Postal Code</label>
<input type="text" data-stripe="address_zip" id="address_zip" required>
</div>
<div class="form-item">
<label for="cc_number">Card Number</label>
<input type="text" data-stripe="number" id="cc_number" required>
</div>
<div class="row row-inner">
<div class="col">
<div class="form-item">
<label for="exp_month">Expiry Month</label>
<input type="text" data-stripe="exp_month" id="exp_month" maxlength="2" required placeholder="00">
</div>
</div>
<div class="col">
<div class="form-item">
<label for="exp_year">Expiry Year</label>
<input type="text" data-stripe="exp_year" id="exp_year" maxlength="2" required placeholder="00">
</div>
</div>
<div class="col">
<div class="form-item">
<label for="cvc">CVC</label>
<input type="text" data-stripe="cvc" id="cvc" maxlength="4" required placeholder="0000">
</div>
</div>
</div>
</fieldset>
<button class="button primary" id="register" data-charge-button>Register</button>
{{ /user:register_form }}
</article>
</section>
{{ section:chargeJS }}
{{ charge:js }}
{{ /section:chargeJS }}
For Workshop entry creation, use the same fields/tags as above but add {{ charge:process_payment }} to the template used to CREATE entries. Do NOT put it on the template used to edit entries.
For a one-time charge, take out the plan part and use {{ charge:data }} for the amount, etc
- Cancel -
{{ charge:cancel_subscription_url }}- creates a URL to cancel a subscription. Pass in thesubscription_id.- example
<a href="{{ charge:cancel_subscription_url :subscription_id="subscription_id }}">Cancel Subscription</a>
- example
- Resubscribe -
{{ charge:renew_subscription_url }}- creates a URL to resubscribe to a subscription. Pass in thesubscription_id - Success -
{{ charge:success }}- was the last action/transaction successful? - Errors -
{{ charge:errors }}- if there were errors, they'll be in here - Details -
{{ charge:details }}- transaction details (all the data from Stripe) - Data -
{{ charge:data }}- to pass transaction data in your form, you can set the parameters (i.e.amount="50") - JS -
{{ charge:js }}- adds the required JS to generate the Stripe token needed
The following subscription data is stored in the user:
customer_id- Stripe customer idcreated_on- timestamp indicating when customer was createdplan: Stripe plan user is subscribed tosubscription_start: timestamp marking the beginning of the subscriptionsubscription_end: timestamp marking the end of the subscriptionsubscription_id: Strip subscription idsubscription_status: status of the subscription. One of:active- subscription is currentcanceled- subscription is inactivecanceling- subscription will not auto-renew atsubscription_endpast_due- payment has failed but subscription not canceled, yet
When a customer's payment fails the first time, you can send an email to the them based on the payment_failed_email_template.
The template has the following variables:
plan- the plan the user is onfirst_name- customer's first namelast_name- customer's last nameattempt_count- how many times you've tried to process their paymentnext_payment_attempt- timestamp of next payment processing attempt
On the last payment failure, their subscription is cancelled and the canceled_email_template is used.
These variables are available:
plan- the plan the user is onfirst_name- customer's first namelast_name- customer's last name
You can have your users update their own payment information or change which plan they are on. Use the charge:update_customer_form tag and pass in the Stripe custormer_id as a parameter.
Like the charge, this form requires the charge:js tag to be on the page so that the Stripe token can be generated. If you want to redirect after success, pass a redirect url.
As w/ the payment form, remember NOT to put name fields on the CC form inputs so they aren't send to the server at all.
Example:
{{ user:profile }}
<header>
<h1>{{ name or username }}</h1>
<h2>{{ email }}</h2>
<img src="{{ email | gravatar:200 }}" alt="{{ name }}" class="img-circle" />
</header>
{{ charge:update_customer_form :customer_id="customer_id" attr="class:form|data-charge-form" }}
<div data-charge-errors></div>
{{ if errors }}
<div class="alert alert-danger">
{{ errors }}
{{ value }}<br>
{{ /errors }}
</div>
{{ /if }}
<fieldset class="payment">
<legend>Payment</legend>
<div class="form-item">
<label for="cc_name">Name on Card</label>
<input type="text" data-stripe="name" id="cc_name" required value="{{ name }}">
</div>
<div class="form-item">
<label for="cc_number">Zip/Postal Code</label>
<input type="text" data-stripe="address_zip" id="address_zip" required>
</div>
<div class="form-item">
<label for="cc_number">Card Number</label>
<input type="text" data-stripe="number" id="cc_number" required>
</div>
<div class="row row-inner">
<div class="col">
<div class="form-item">
<label for="exp_month">Expiry Month</label>
<input type="text" data-stripe="exp_month" id="exp_month" maxlength="2" required value="{{ exp_month }}">
</div>
</div>
<div class="col">
<div class="form-item">
<label for="exp_year">Expiry Year</label>
<input type="text" data-stripe="exp_year" id="exp_year" maxlength="2" required value="{{ exp_year }}">
</div>
</div>
<div class="col">
<div class="form-item">
<label for="cvc">CVC</label>
<input type="text" data-stripe="cvc" id="cvc" maxlength="4" required placeholder="0000">
</div>
</div>
</div>
</fieldset>
<button class="button primary" id="update-payment" data-charge-button>Update</button>
</article>
{{ /charge:update_customer_form }}
{{ /user:profile }}