Skip to content

Latest commit

 

History

History
97 lines (65 loc) · 5.17 KB

STRIPE.MD

File metadata and controls

97 lines (65 loc) · 5.17 KB

Using the Stripe API in a Django application for processing recurring subscription payments

This application will offer a premium subscription service that users can pay for with Stripe. Premium subscriptions will give users unlimited access to the API as well as access to certain parts of the web UI that regular users do not have access to.

Our application will have the following user tiers:

  • Unauthenticated guest users
  • Free tier users (signed up with LinkedIn)
  • Premium users (signed up with LinkedIn and payed with Stripe)

Before looking at the Stripe API, here's a brief overview of what I would like to be able to do:

  • Build a view where signed-up users can purchase a Premium Plan
  • API access for a signed-up user will be determined by their premium subscription status (non-active, active, cancelled)
  • Users can cancel their premium subscriptions at any time

Resources

This seems like a good place to start: https://stripe.com/docs/billing/subscriptions/fixed-price.

This example has a GitHub repo with an example backend implementation in Flask that should be a good reference for doing something similar in Django.

For the frontend this repo has examples for vanilla JS as well as React. These may also be helpful for our implementation in Vue.

Stripe business setup

First we will need to setup a business in Stripe in which we can create our service.

Once we have setup the account, we will want to add the test API keys to our .env file. For the production keys, we can add these to the backend and NGINX Dockerfiles using ARG and ENV, and we can also add them in the docker build commands of the .gitlab-ci.yml file.

High-level Overview

Here's a high level overview of what we need to setup recurring billing for our users:

Stripe account

  1. A Stripe account with a business setup in Stripe.
  2. test API keys in local development (publishable key in frontend, secret key on backend)
  3. live API keys in GitLab secrets, .gitlab-ci.yml, Dockerfiles and docker build commands as --build-args

Backend

  1. stripe package installed from PyPI
  2. stripe listen --forward-to localhost/api/stripe-webhooks/ command running using CLI (the key shown in the output of this command must be added as the STRIPE_WEBHOOK_SECRET)
  3. django management command to create webhook used in production using webhook API.
  4. django management command to create prices and products
  5. save the price_id of the subscription
  6. dj-stripe to sync Stripe data with Django models
  7. endpoint for Stripe webhooks to listen for events that happen (this can be used with dj-stripe)
  8. endpoint for create_subscription
  9. endpoint for cancel_subscription

Frontend

  1. Add stripe via CDN to index.html: <script src="https://js.stripe.com/v3/"></script>
  2. Add a Stripe.vue component

Frontend Stripe.vue component

  1. Instantiate stripe with Stripe("PUBLISHABLE_KEY")
  2. Define elements as stripe.elements()
  3. In the mounted hook, create and mount the card variable
  4. Define a purchase function that is called on a button click when card information is filled out and the customer is ready to purhcase
  5. Inside the purchase function, call stripe.createPaymentMethod that takes in the card information.
  6. When stripe.createPaymentMethod completes, call our custom createSubscription function.
  7. Alternatively, use a package like https://www.npmjs.com/package/vue-stripe-elements-plus to simplify some of the logic

createSubscription function

  1. This function needs to take in the paymentMethod.id that we get from the result of stripe.createPaymentMethod
  2. This function will POST to our Django backend's /api/stripe/create-subscription/ route (create_subscription)

/api/stripe/create-subscription/ backend route

  1. This function does three things:
  2. get or create the Stripe Customer ID (which is stored on the user model; request.user.stripe_customer_id) using stripe.Customer.create
  3. Attach the payment method to the customer with stripe.PaymentMethod.attach
  4. Set the default payment method on the customer with stripe.Customer.modify
  5. Create the subscription with stripe.Subscription.create
  6. Finally, this view returns the results of stripe.Subscription.create (JsonResponse(subscription)).
  7. We may want to save additional information from the subscription on our User model, such as the expiration date, or update a subscription_status flag for checking permissions when accessing gated resources.

Webhooks for subscription renewal

  1. An event with type invoice.paid is sent to the webhook,
  2. Get the update the valid_through field on our Subscription (Django) model.

Cancelling a subscription

  1. Setup and endpoint for cancel_subscription
  2. Call stripe.Subscription.delete in this endpoint
  3. Delete the Django model subscription with request.user.subscription.delete()

TODO

  • Test subscription renewals
  • Figure out how to test things in CI, how to mock stripe library calls