Masonite Billing
Requires
Masonite 2.0.0+
Installation
Installing Masonite Billing is simple. We just need a new configuration file, 2 new migrations and extend our User model.
Pip Install
Add the Service Provider
Simply add the Masonite Billing Service Provider to your providers list:
This will add a new install:billing command to craft. Just run:
This will create a new configuration file in config/billing.py
Configuration File
All billing information will be located in the config/billing.py file and has 2 important constants:
The DRIVER is the processor that Masonite billing will use and the DRIVERS constant is the configuration setting for the driver.
Although there is the DRIVER constant, Masonite Billing currently only supports Stripe. Other drivers like Braintree will be supported in later releases which should be backwards compatible.
Migrations
We'll need to create 2 new migrations: one to add columns to the users table and one migration to create a new subscriptions table. Just create these migration files with craft and copy and paste the migration into those files and migrate them.
Let's first add 2 new columns to our users table.
Now just add this column to the migration file:
Now let's add a new subscriptions table.
Now just migrate the new migrations:
Stripe Authentication Keys
Just add your Stripe public and secret keys to your .env file:
Billable Model
Masonite Billing consists of a model that should be inherited by whatever model you want to add subscription billing information to. In this example here, we will focus on adding the billing integration to our User model.
Once that is added we will now have a plethora of methods we can use to subscribe and charge our user.
Read more about how to handle subscription and payment information in the Usage documentation.
Getting Started
Below you will notice we are using a tok_amex token, you may use this token for testing purposes but this token in production should be the token returned when processing your stripe form.
It's also important to note that the subscription records in your database are never deleted but are updated instead. So canceling a subscription does not delete that subscription from your database but only sets when the subscription ended. This is good if you want to dump the data into an email campaign tool to try and get back any lost customers.
Subscribing to Plans
To subscribe a user to plans, we can use the subscribe method like so:
This method retuns a string of the subscription token such as sub_j8sy7dbdns8d7sd..
If you try to subscribe a user to a plan and the plan does not exist in Stripe then Masonite will throw a billing.exceptions.PlanNotFound
exception.
Checking Subscriptions
If you want to check if the user is subscribed you have a few options:
You may check if the user is subscribed to any plan:
or you can check if the user is subscribed to a specific plan:
You may also check if a user was subscribed but their plan has expired:
or you can obviously check if the user was subscribed to a specific plan:
Getting The Plan Name
If you need to get the name of the plan the user is on you can do so using the plan() method:
This will return the name of the plan in Stripe, not the plan ID. For example, our plan ID might be masonite-test but the plan name could be "Awesome Plan."
Trialing
If the plan you are subscribing a user to has a trial set inside Stripe then the user will be automatically enrolled in a trial for that time. We can check if the user is on a trial using the on_trial() method. In our examples here, the masonite-test plan has a 30 day free trial set inside Stripe.
For example:
We may also want to skip the trial and charge the user immediately for the plan:
We can also specify the amount of days the user should be on a trial for when subscribing them to a plan:
Swapping Plans
We can swap subscription plans at anytime using the swap() method:
Canceling
If you want to cancel a user's subscription we can use the cancel() method.
This will cancel the users plan but continue the subscription until the period ends.
Notice here that the last is_subscribed() method still returned True even after we canceled. The user will continue the subscription until the period ends. For example if it is a 1 month subscription and the user canceled 1 week in, Masonite Billing will continue the subscription for the remaining 3 weeks.
If you wish to cancel the user immediately we can specify the now=Trueparameter:
The period between canceling a subscriptions and the period running out can be caught using the user.is_canceled() method:
Again this will only return true if the user has an active subscription but has chosen to cancel it. If the subscription is canceled immediately, this will return False.
Resuming Plans
If you don't cancel a plan immediately, there will be a period between when the user canceled and when the plan can be resumed. We can use the resume() method here:
Updating Card Information
Sometimes a user will want to switch or update their card information. We can use the card() method and pass a token to it:
Charging users
If you want to make one off transactions for customers you can do so easily using the charge() method:
You can charge a card by passing a token:
You can also charge a customer directly by leaving out the token which will charge whatever card the customer has on file.
You can also add a description and metadata (as a dictionary) for the charge:
Coupons
You will first need to setup coupons in Stripe.
Once you setup a coupon you can use coupons on both charges and subscriptions:
You can also pass in an integer to deduct an amount:
This will deduct 5 dollars frin the 10 dollars you are charging the user.
You can also make a coupon for a certain percentage reduction:
This will deduct 25 percent off.
Webhooks
Webhooks allow your application to interact directly with stripe when certain actions happen such as when a user's subscription has expired. You can use these webhooks to catch any events emitted.
Getting Started
Masonite Billing also allows you to tie into Stripe webhooks. If the subscription is cancelled in Stripe, Stripe will send your server a webhook and Masonite Billing will update the database accordingly.
Be sure to setup the correct url in the the Stripe dashboard inside your Webhook Settings. The url to setup should be http://your-domain.com/stripe/webhook
or if you're testing with Ngrok it should be something like http://684b1285.ngrok.io/stripe/webhook
.
If you go the Ngrok route be sure to read the Testing With Ngrok section
Webhook Controller
We can simply put the webhook controller in our routes/web.py file just like any other controller but it can point to the packages controller:
This will send all Stripe traffic to the server and handle any hooks accordingly.
Testing With Ngrok
Most developers use Ngrok for testing incoming webhooks. Ngrok is a freemium HTTP tunneling service that allows extrernal requests to tunnel into your localhost and port environment. It's exellent for testing things like this as well as other things like OAuth.
If you use Ngrok you will get a subdomain like: http://684b1285.ngrok.io
. Because this is a subdomain, Masonite needs to know which subdomain to support so you're route will have to be:
in order to catch the Ngrok subdomain. This setup will allow you to send test webhooks from Stripe to your server.
You can read more about subdomains in the Subdomain Routing section of the Routing documentation.
Csrf Protection
External incoming API calls typically will not be able to be CSRF protected because they will not know the specific token connected to a request. We will need to put an exception to the /stripe/webhook route:
Creating Custom Hooks
Currently the webhook controller only handles when a subscription is canceled but can handle any hook you like. If you need to create a custom hook you can inherit from the WebhookController and add the needed controller methods:
Routes
You'll also have to specify a new location of your controller which should now be located in your normal controllers directory:
Events
Events are specific types of webhooks that Stripe will send out when certain actions occur.
You can see a list of stripe events here: https://stripe.com/docs/api#event_types
You'll notice that we have a handle_resource_event method. The WebhookController will take all incoming webhook events into the handle method and dispatch them to other methods.
How it does this is simply takes the event lets say the charge.dispute.created Stripe event (see the link above for a list of more events) and parses it into a string that looks like:
You'll notice we just replaced the . with _ and prefixed a handle to it. This looks like a method call. If we simply create a method now:
This method will be called whenever we receive a webhook for that event. If no method is found for the incoming webhook then the server will return a Webhook Not Supported string which you may see while development testing your Stripe webhooks.
Last updated