Bootstrapping Guide

Integrate payments to your first Saas and accept payments with @polar.sh

why polar sh

Well, Stripe is invite only in India and Lemon Squeezy needs pre-approved by Stripe plus Stripe onboarding sucks too, I messaged them had some meetings like months at end they said I need some kind of business registration bro it's side hustle I don't know I need a business I just need to implement payment easy fast and get paid easy peasy, now comes merchant of record who accept payment on behalf of use and do all the tax and stuff we dont care about and pay us cool right? There's one doing the same thing: polar.sh (an open-source Merchant of Record). There are alternatives too, but I didn't try them, so let's stick to polar.sh for now.

Plus their UI is top notch

just 3 easy steps

  • Login to polar sh, add a product and get the accessToken
  • A button named buy now
  • Webhook handling for subscription events

first steps

Assuming you have auth setup in your project, you would have the client email in your state. Now login to Polar.sh - their onboarding process is very simple. Select your country carefully. Initially, I messed up the first time and selected US, which caused some issues as my bank is in india. But their support is one of the best (support@polar.sh), so make sure to select the country where your bank account is located.
once the account is done go to polar or sandbox.polar.sh(testing) and after that go settings → general scroll down to bottom there is developer create new tokens
Create one, Copy the token and save it somewhere safe. You won’t be able to see it again.

now setup your env

//.env.local
POLAR_ACCESS_TOKEN = polar_oat_your_token_here
install the deps
pnpm install @polar-sh/sdk @polar-sh/nextjs 
Setup client
// src/polar.ts
import { Polar } from "@polar-sh/sdk";

export const api = new Polar({
  accessToken: process.env.POLAR_ACCESS_TOKEN!,
  server: "sandbox", // Use this option if you're using the sandbox environment - else use 'production' or omit the parameter
});

Now Create a buy now button

const BuyNowButton = () => {
  const handleBuyViaPolar = () => {

    const productId = "get_from_polar_dashboard";
    const checkoutUrl = new URL("/checkout", window.location.origin);
    checkoutUrl.searchParams.set("products", productId);
    // this is optional but recommended like when you open the checkout page this email will be autofilled in the checkout page and any additional parameters will be returned in webhook call for you to play with
    if (isAuthenticated && user) {
      if (user.email) {
        checkoutUrl.searchParams.set("customerEmail", user.email);
      }
      if (user.id) {
        checkoutUrl.searchParams.set("customerExternalId", user.id);
      }
    }
    window.open(checkoutUrl.toString(), "_blank");
  };
  return <button onClick={handleBuyViaPolar}>Buy this</button>;
};
Now when user clicks on this button it will navigate to /checkout

Checkout Route

// src/app/checkout/route.ts
import { Checkout } from "@polar-sh/nextjs";

export const GET = Checkout({
  accessToken: process.env.POLAR_ACCESS_TOKEN!,
  successUrl: "/confirmation?checkout_id={CHECKOUT_ID}", // the polar sh will navigate back to this URL after the payment is successful
  server: "sandbox", // Use this option if you're using the sandbox environment - else use 'production' or omit the parameter
});
And this will navigate to the polar sh payment page

final step webhook setup

Now we need to create an API route which will listen to webhook so that when payment is success we can get to know
Go to polar sh dashboard → settings → webhook → add endpoint that will be called by the webhook format → raw (or whatever) select all or maybe as per you need and copy the webhookSecret

update .env

//.env.local
POLAR_WEBHOOK_SECRET = polar_whs_your_webhook_secret
// src/app/api/webhook/polar/route.ts
import { Webhooks } from "@polar-sh/nextjs";

export const POST = Webhooks({
  webhookSecret: process.env.POLAR_WEBHOOK_SECRET!,
  onOrderPaid: async (payload: any) => {
 
    console.log("🎉 WEBHOOK TRIGGERED: onOrderPaid");
    const customer = payload.data?.customer;

    try {
      await markUserAsPaid(customer.id) // or whatever logic you need to run
    }catch(error){
      console.log('error in onOrderPaid',error);
    }      
  }
});

run the server

If you are in production use your domain else use ngrok in development
ngrok http 3000
//this will give you a URL like this https://4901-103-240-13-185.ngrok-free.app add your API route at end so the full will be 
https://4901-103-240-13-185.ngrok-free.app/api/webhook/polar
https://your-domain.com/api/webhook/polar

test payment

Now when a user does the payment it will call your webhook which will call the function and your db is updated that's all you need to get your first paid user now build amazing stuff and get paid.

Last Note

I'm not a super Payment integration expert, its just personal experice and hope it helps you.
Go Back