Invoices, Taxes and Payments
In this guide, we're going to go through the steps you need to take in order to create Invoices, add Taxes, and finally take Payment for your Subscriptions.
Once you've gone through all the steps, you should have a good understanding of how to:
- Import datasets into Elastic Path Subscriptions to set up your store
- Model an Offering that requires Tax to be added to the Invoice
- Create Invoices with a Billing Run
- Add taxes to those invoices with a Tax Run
- Take payment for Invoices with a Payment Run with both Elastic Path Payments and Manual Payments
Prerequisites
Store Setup
Make sure your store has USD
, EUR
, and GBP
currency codes set up.
We'll be using each of these codes to model different Taxes across different authorities.
Download the Assets
Download this zip file which contains the following:
File | Description |
---|---|
collection.json | A Postman Collection with all the API calls required for this guide |
environment.json | A Postman Environment you can set up to point to your store |
manual-payment-store-dataset.jsonl | A dataset file you can import into Elastic Path Subscriptions if you're using Manual Payments |
ep-payment-store-dataset.jsonl | A dataset file you can import into Elastic Path Subscriptions if you're using Elastic Path Payments |
Payment Provider Setup
For this guide, we've set up two different approaches on how you can take payment for your invoices and suggest you pick the one that best suits your production needs.
- Elastic Path Payments
- Manual Payments
If you are planning on using Elastic Path Payments with your store, there are a few prerequisites you need to do before continuing with the tutorial.
First, make sure you have Elastic Path Payments enabled for your store.
Second, you will need to create the following three customers in Stripe Test mode, along with some dummy card
information (you can use the card number 4242 4242 4242 4242
with any future expiry date and CVC):
- Albert Einstein
- Isaac Newton
- Robert Oppenheimer
Make note of their Stripe Customer IDs and associated Card ID.
Finally, open up ep-payment-store-dataset.jsonl
in your IDE or text editor.
You'll notice that on lines 4-6, we define the subscriptions where payment_authority
has a few placeholder
values for you to modify.
{
"type": "elastic_path_payments_stripe",
"customer_id": "<STRIPE_CUSTOMER_ID>",
"card_id": "<STRIPE_CARD_ID>"
}
Replace <STRIPE_CUSTOMER_ID>
and <STRIPE_CARD_ID>
with the Stripe information for each Customer you just created.
For a production use case, your checkout process would handle the creation of these customers and cards in Stripe, but for the purposes of this guide we're doing it manually.
Manual Payments are used when you want to handle all payment processing outside of Elastic Path meaning you will
not have to set up any payment gateways or adjust the import file manual-payment-store-dataset.jsonl
.
Instead, you will be doing an extra step at the end of this tutorial to mark each payment as successful via an API call.
For a production use case, you would use this approach if you had a system that handled all payment processing for you, or you wanted to use a payment gateway that Elastic Path doesn't support.
Manual Payments in the context of Subscriptions are not the same as the Manual Payment Gateway for Carts & Orders.
They follow a similar concept, but also means you are responsible for making sure Subscribers payment details can be used long term and not just part of the initial payment.
Import the Postman Collection and Environment
Finally, let's set up the Postman Collection and Environment file.
Import collection.json
and environment.json
into Postman, and go to the Elastic Path Subscriptions - Adding Taxes
environment.
Change the Current Value of the following variables to match your store, creating a new API key in Commerce Manager if you haven't already got one:
Variable | Value |
---|---|
clientSecret | The Client Secret for your API key |
clientID | The Client ID for your API key |
authURL | The API Base URL for your store |
To check everything is set up correctly, go to the Elastic Path Subscriptions - Adding Taxes
collection, ensure that the
Elastic Path Subscriptions - Adding Taxes
environment is selected and run the first API request called Authenticate
.
You should see a 200 OK
response and a new access token created in your environment for subsequent requests.
Our Fictional Business Problem
Let's get started by outlining the problem we're trying to solve.
- You own a company called GlamGlobe which sells access to its makeup tutorials all around the world
- There is only one monthly plan available called GlamPass, giving you access to all of its content on a monthly cadence
- Because your Subscriptions are located across many countries, you are going to have different taxes applied to each Subscriber
- You already have a tax system that can handle this for you, but you need a way to add those taxes to invoices before payment is collected
- Taxes for the EU are added into the price of the Subscription so we don't have to add anything to the invoice, but this is not the case for the US and UK
Step 1 - Importing Offerings & Subscriptions into a Store
Let's start by setting up a store with all the information we need for our Subscriptions.
Elastic Path Subscriptions supports the ability to model most of your Subscription entities in a JSON Lines file that can then be imported into your store.
JSON Lines is a text format where each line is a JSON object, which is great for importing large datasets with relational information between objects in a single file.
We include support for importing the following Subscription entities:
- Products
- Plans
- Offerings
- Subscribers
- Subscriptions
Depending on which approach you're using (Elastic Path Payments or Manual Payments), you'll find a .jsonl
file in the zip you
would have downloaded in the prerequisites section.
In Postman, go to the Import the Dataset
API request and in the Body
tab, change the value of import_file
to
select the dataset you want to use (ep-payment-store-dataset.jsonl
for Elastic Path Payments or
manual-payment-store-dataset.jsonl
).
Run the request and you should see a new subscription_import
job created with status:pending
.
If you then run Check the Import Status
request, you should eventually see that the status
of your import job is
successful, with meta.records.imported
showing the following:
{
"imported": {
"subscription": 3,
"subscription_offering": 1,
"subscription_plan": 1,
"subscription_product": 1,
"subscription_subscriber": 0
}
}
If you then check Commerce Manager, you'll see that you now have a single offering called GlamGlobe, which has a GlamPass Product and a simple Monthly Plan.
You'll notice you also have three famous Subscribers, each an active subscription.
This can also be validated in Postman by running List Subscriptions
.
Step 2 - Understanding Taxes in Elastic Path Subscriptions
Let's take a look at the Offering we've just imported. Open up the file you just imported into Elastic Path Subscriptions, and take a look at the first line which should be a
subscription_product
.
{
"data": {
"type": "subscription_product",
"attributes": {
"external_ref": "glam_pass",
"name": "GlamPass",
"sku": "glam_pass",
"price": {
"GBP": {
"amount": 1200,
"includes_tax": false
},
"USD": {
"amount": 1500,
"includes_tax": false
},
"EUR": {
"amount": 1400,
"includes_tax": true
}
}
}
}
}
You'll notice that for the GBP
and USD
currency codes, we have set includes_tax
as false
, but EUR
we've set it
to true
.
This allows us to model for the scenario where our EUR
Subscribers either have tax included in the price of the
Subscription, or alternatively, no tax is actually required within that jurisdiction
On the other hand, for USD
and EUR
subscribers, we've indicated that tax is not included in the price, and we're
going to have to add it to the invoice before taking payment.
When you set includes_tax
to true
, you will not be able to take payment for that invoice until you have added taxes
to it (even if it's an empty array of Tax Items).
Step 3 - Creating Invoices with a Billing Run
If you run the List Invoices
request in Postman, or check Subscriptions -> Billing & Payment -> Invoices
in
Commerce Manager, you'll notice that there aren't any there yet, even though there are active Subscriptions.
This is because of two reasons:
- These imported Subscriptions did not have
first_invoice_paid
set totrue
, indicating that the first Invoice would have been paid at checkout (either in Carts and Orders or your own checkout process) - We haven't triggered a Billing Run yet
Billing Runs are used to generate the next Invoice for each of your Subscriptions and are asychronous jobs that you can set up to run on a schedule (e.g., once a day at 1:00am).
For the purposes of this guide however, we're going to start one manually.
In your Postman collection, run the request called Trigger a Billing Run
, followed by Check the Billing Run Status
.
The first call will schedule a Billing Run to start immediately, where the second one will check the status of that job.
Once your Billing Run has successfully completed, let's take a look at the report meta:
{
"invoices_ready_for_payment": 1,
"invoices_tax_required": 2,
"total_ready_for_payment": {
"EUR": {
"amount": 1400,
"includes_tax": true
}
},
"total_tax_required": {
"GBP": {
"amount": 1200,
"includes_tax": false
},
"USD": {
"amount": 1500,
"includes_tax": false
}
}
}
You'll notice that the Billing Run has reported that there is one invoice ready for payment for €14, and two invoices that require tax to be added for £12 and $15 respectively.
We can also see the invoices it's created in either Commerce Manager (under Subscriptions -> Billing & Payment -> Invoices
), or by running List Invoices
in Postman.
Take note of the following fields for each Invoice:
data.attributes.outstanding
- Indicates if an Invoice has been paid (false
) or not (true
)data.attributes.tax_required
- Indicates if an Invoice requires tax to be added (true
) or not (false
)
You'll notice that all have outstanding
set to true
, but only the EUR
invoice has tax_required
set to false
.
Step 4 - Triggering a Payment Run without Tax Set
Now, what happens if we try to run a Payment Run without adding tax to those Invoices that require it?
Run the Trigger a Payment Run
request and then Check the Payment Run Status
until the job is successful.
Let's take a look at our invoices again by running the List Invoices
request.
- Elastic Path Payments
- Manual Payments
You'll notice that only one of our invoices has been marked as outstanding
set to false
, which is our EUR
invoice
that had tax included in the price.
The two invoices that required tax to be added are still outstanding
, which we'll go over how to do in the next
section.
You'll notice that all the invoices are still outstanding
, but the EUR
invoice has manual_payment_pending
set
to true
indicating that a pending payment has been set up for this invoice for your external system to handle and
report back on.
The other two invoices that required tax however have manual_payment_pending
set to false
, meaning you still need
to add tax to them before payment can be collected.
Later on in this guide, we'll also go over how you can complete this payment cycle for your manual payments.
Step 5 - Adding Taxes to Invoices with Tax Runs
While one of our invoices had payment collected, the other two are still outstanding because we've not added any taxes to the invoices yet.
To do this, we need to trigger a Tax Run that will add Tax Items to each of our invoices.
Tax Items in Subscriptions are the same as you get in (Carts & Orders)[https://elasticpath.dev/guides/How-To/Carts/Taxes/create-tax-rates], allowing you to specify a rate that will be added to the base price of the invoice.
{
"code": "NY_SALES_TAX",
"jurisdiction": "New York, USA",
"name": "New York State Sales Tax",
"rate": 0.04,
"type": "tax_item"
}
In a real world scenario, you would want to build an integration that listens to Invoice Created events, checks if tax is required, calculate it, then batches them up to be sent as part of a Tax Run.
For the purposes of this guide however, we're going to do it manually as we only have two invoices that we need to add Taxes for.
If you run the List Invoices
request again, copy the id
field for each of the invoices that have tax_required
set
to true
, making a note of which invoice is for which currency (one for USD
, one for GBP
).
Next, go to the Postman request Start a Tax Run
and open up the Body
section.
Replace each invoice_id
with the associated ids you've just copied and run the request.
If you check the status of that Tax Run by running Check the Tax Run Status
, you should see
meta.report.invoices_updated
has been set to 3
.
When you go back to look at those invoices in either Commerce Manager or by running List Invoices
, tax_required
will
be set to false
and you'll see the Tax Items you've just added to each invoice under data.attributes.tax_items
.
Step 6 - Collecting Payment for Invoices with Taxes Added
Finally, let's trigger another Payment Run to collect payment for those Invoices we've just added taxes to.
Run the Trigger a Payment Run
request again and then Check the Payment Run Status
until the job is successful.
- Elastic Path Payments
- Manual Payments
If you've been using Elastic Path Payments for this tutorial then payment should have been successfully collected for all of your invoices!
You can check this in either Commerce Manager, or by running List Invoices
again.
outstanding
will have been set to false
for each of your invoices, and if you check Stripe you should find a record
of Payment for each of your Subscribers.
You will also have had a subscription_invoice_payment
object created for each of your invoices.
Let's check this out.
First, run the Get invoice
in Postman. You'll notice it returns one of the three invoices you've just taken payment
for, where outstanding
is now false
.
Next, run Get Payments
. You'll see a single subscription_invoice_payment
object where success
has been
marked as true
, with some associated meta
about what time the payment was collected and the amount paid.
As before with our EUR
invoice, data.attributes.manual_payment_pending
should now have been set to true
for each
of our three invoices.
Let's look at what we need to do to mark these payments as successful.
In a production setting, you would typically have an integration that will do this for you, but for the purposes of this guide we'll go through the steps of just marking one of the invoices as successfully paid.
In Postman, run Get Invoice
to return one of the invoices where we can see that manual_payment_pending
has been set
to true
.
Next, let's run Get Payments
, which will use the Invoice ID you just saw, and retrieve all payments associated with
that Invoice.
You'll notice that there is a single subscription_invoice_payment
object where attributes.pending
is currently
true
.
To mark this payment as successful, you want to run the Mark Payment as Successful
request, which will mark that
pending payment as successful.
To verify everything worked as expected, re-run the Get Invoice
request. You should see that both outstanding
and
manual_payment_pending
have both been set to false
, indicating that payment has been successfully collected.
What Have we Learned?
In this guide, we've covered the basic workflow for managing Invoices, Taxes, and Payments in Elastic Path Subscriptions for both Elastic Path Payments and Manual Payments.
You should know how to specify whether a Subscription requires Tax Items to be added to the Invoice before payment can be taken.
You should understand the difference between Payment, Tax, and Billing Runs, and what Tax Items are.
Finally, you should know the differences between Invoices being marked as outstanding
and tax_required
, and once
payment has been collected, how to access the payment record against an Invoice.