Was quite a week for me last week. The holiday weekend led to illness during the week. Not Covid, thankfully, but enough of an illness to really put a damper on working. Ended up shutting everything down on Friday to focus on resting and trying to recover. Still not doing great, but was able to get a few things done over the weekend while not pushing myself too hard.
My main goal: get payments set up and working on whoshouldiunfollow.com. Somehow, that’s never quite as easy as I think it should be, but I was able to get things setup and working finally. I use Stripe for my payment processing and so there were a few things I had to do.
First, I had to update my database models for my user to keep track of their subscription status. This isn’t too bad, I just hold on to the stripe id and their subscription status. But how does that get updated?
There are two parts that come into play, since I use Stripe Checkout as a pretty easy way to do it. The first part is creating the checkout session with Stripe. Here’s how I did it:
I started by creating a mutation that I could use as a hook. Blitz uses mutations as a way to create functions that can modify database values, and in this mutation, I do a couple of things.
I create a Stripe customer. I can probably modify this to figure out whether or not they are an existing customer, but I haven’t spent a lot of time on that, and this is how I have done it previously, so just used most of the code I had written previously. This is a simple API call to Stripe:
const customer = await stripe.customers.create({ email: user.email ? user.email : email, })
Once I have the customer, I can update my user object with the Stripe customer ID:
await db.user.update({ where: { id: ctx.session.userId }, data: { stripeCustomerId: customer.id, }, })
Then, I can create a checkout session on Stripe and return the session ID:
const lineItem = { price: priceId, quantity: 1, } const session = await stripe.checkout.sessions.create({ customer: customer.id, mode: "subscription", payment_method_types: ["card"], line_items: [lineItem], success_url: `${env.DOMAIN}/unfollow?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${env.DOMAIN}/upgrade?cancel=true`, allow_promotion_codes: true, }) return { sessionId: session.id, }
That’s the main flow for creating a checkout session. Most of that is just copied over from the Stripe documentation, with customization of the URLs that users are redirected to on success and cancel actions. And the priceId is the ID given to the product you create on Stripe. I have it setup as an environment variable that gets passed into the mutation when the user selects the plan that they want. I’ve got 2 plans set up to start: a $1/month plan that let’s users identify the dormant followers they have and they’ll be able to remove them 1 by 1 manually and a $5/month plan that will allow them to remove them all in bulk. So each of those plans is setup as a product in Stripe, and passes that value into the mutation as the priceId that gets added to the lineItem variable.
Then the session ID gets passed back to the calling function on the frontend and is passed on to stripe in order to redirect to the checkout flow:const result = await stripe.redirectToCheckout({ sessionId, })
But what happens after the user goes through the checkout flow?
That’s where the second part comes in: the webhook.
The webhook is the part of your application that Stripe calls. So after the user puts in their payment information, you have a route that you can add to your Stripe account that they can call with the information you need. Of course, there are all sorts of events that you can have sent, but I don’t need most of them. So I setup my webhook to only be sent two types of events: checkout events and customer events. Basically, I want to know when a checkout session is completed, so I can set the database record to have a subscription status of “active” and I want to know when the customer’s subscription is created, updated, or deleted, so I can update the status accordingly.
Here’s the relevant code:
switch (event.type) {
case "checkout.session.completed": {
console.log("💰 Webhook: Checkout session completed")
const session = event.data.object as Stripe.Checkout.Session
// set user's subscription status to active
await db.user.update({
where: { stripeCustomerId: session.customer as string },
data: {
stripeSubscriptionStatus: "active",
},
})
break
}
case "customer.subscription.created":
case "customer.subscription.updated":
case "customer.subscription.deleted": {
console.log("💰 Webhook: Customer subscription updated")
const subscription = event.data.object as Stripe.Subscription
//update user's subscription status
await db.user.update({
where: { stripeCustomerId: subscription.customer as string },
data: {
stripeSubscriptionStatus: subscription.status,
price: subscription?.items?.data[0]?.price.id,
stripeSubscriptionId: subscription?.items?.data[0]?.id,
},
})
break
}
default:
}
So Stripe sends those events to the webhook and my database gets updated accordingly. Then the user gets redirected to the success page, which in this case is the table of users who are dormant.
Next up is a feature to allow the user to protect certain users from being unfollowed, which is something I realized I need personally. Some of the people I follow that haven’t tweeted, I don’t necessarily want to unfollow. Like my mom, for example. I don’t actually know if she really gets on Twitter very often, but I did follow her back when she followed me. And if she happens to visit my profile and sees that I no longer follow her, probably not great…
I’m probably way overthinking this, but it’s all good. This actually ties into what I want to do down the line with allowing users to create various groups of people. So that’s the justification for including it this early on. Almost done with that bit, so should be going live soon, depending on how I’m feeling and how much extra stuff comes in from the day job this week. Going camping this weekend with my youngest and his cub scout troop, so not going to have much time to code and I’m totally cool with that! Looking forward to getting out in nature for a bit.
Other Developments
A couple of big announcements! Or rather, 1 big announcement and 1 thing I think is pretty cool that you might enjoy.
First up, the announcement! I’ve been thinking about launching a course. I’ve actually been working on the course for a long time, ever since I went through the On Deck Course Creator fellowship last year. I had a couple of false starts, released a tiny self-paced course on Teachable that got 1 sale for $20, ran a cohort of a course on automation that made $1000, and I’ve been trying to improve on what I did ever since. But I haven’t been satisfied with what I had. So I’ve really been digging deep, while exploring all sorts of topics, course models, and ideas.
And I finally figured it out. All it took was me buying a lifetime license for a new SaaS offering that I really liked because that was the motivation I needed to get started. The tool is called Heartbeat, and it’s a great community tool, but the selling point for me was the course functionality built-in alongside the community bits. That allows me to build in the course as I go, while keeping the community running. I’m going to be opening up the community sometime in the next week, as I start getting things put together. The course called How To Scale Yourself, and is going to have 5 modules to start:
An introduction - my story, why I’m doing this, and what to expect from the content. Then I’ll introduce each of the four modules of the course, the first three being the pillars of what I believe it takes to scale your impact, and the 4th a plan for doing it in public.
Pillar 1: Code - this is not limited to just coding, it actually refers to automation in general. If you can automate pieces of your life to ensure that things can happen without you thinking about them, that’s the first component to scale. You can accomplish a ton by just automating a few small things you do on a regular basis. This will draw from the cohort-based course I ran called Supercharge Your Time.
Pillar 2: Connections - the second way to scale yourself online is to connect with people. And I mean truly connect, not just collect people as followers, or LinkedIn connections, or Facebook friends. For this, you want people to know what you care about, what motivates you, and what you’re striving for. Because then they can help you when you need it, and not only will they be able to, they’ll want to.
Pillar 3: Content - this is the final pillar, and the one that a lot of people think of. But too many people are striving for the wrong things. They want to chase the big numbers: the 1000s of reads, the millions of views. But that’s not where the gold is online. There is so much value in the details that only you can provide. Too many people are competing on blog structure, or video format, or having the best audio equipment and editing. But I think the most value is in the raw, nitty-gritty details, the struggles you face, the way you figured out to overcome them. Focus on uniqueness over quality, because that’s where you grow the most.
Final section: How to Build In Public. This is where I’m going to share the framework I use. It allows me to minimize the effort I have to put in but maximize the results, because I focus on the stuff that matters and I do so in a system that allows me to flourish. And you don’t need to be building a product, or a startup, or any sort of business to use it. It’s really about building yourself into the future self you want to be and maximizing your potential in the manner that you enjoy the most.
All subscribers are going to get free access to the course/community for life. Starting out, I’m going to price the it the same as I’ve currently got the Substack: $10/month. I am planning on doing some pricing experiments as it grows, because I think there are some interesting ways to grow communities online now. But the current plan is to keep it at $10/month or $100/year until I’ve got the course material finished. Once the full course is live (thinking it will probably take me 3 months or so to get there), I’ll be bumping the course to $25/month. After that, I’ll be evaluating the price every 3 months. And I’m planning on offering a lifetime membership at 10x the next monthly price, so I’ll have that planned out 3 months in advance. So starting out, I’ll be offering lifetime access for $250 and when I bump to $25/month, I’ll set the next bump that will happen in 3 months and offer the lifetime deal for 10x that monthly price. And so on.