Skip to content

Paddle Billing API and Webhooks wrapper with detailed TypeScript types

License

Notifications You must be signed in to change notification settings

kossnocorp/paddle-billing

Repository files navigation

Paddle Billing

Paddle Billing API, Webhooks and web (Paddle.js) wrapper with detailed TypeScript types.

Installing

The library is available as an npm package.

To install the package run:

npm install paddle-billing

The minimum required Node.js version is v18, as it uses Fetch API. It's possible to use older versions with global.fetch polyfill, but not recommended.

Usage

API

paddle-billing wraps all available Paddle Billing API methods, replicating the naming structure. Every API component (query, body, response, etc) is carefully typed, so you can use those as the documentation or read the Paddle API Reference for more details.

To use a method, create a client with authentication details and call the method with it:

import { client, cancelSubscription } from "paddle-billing";

const paddle = client("PADDLE_SECRET");

cancelSubscription(paddle, "SUBCRIPTION_ID").then((subscription) => {
  if (subsription.error) {
    // The request failed:
    console.error(subscription.error); // See PaddleAPI.Error
    return;
  }

  // Do something with the subscription:
  subscription.data;
});

Sandbox

To use the Sandbox, pass true as the second argument to client:

import { client, cancelSubscription } from "paddle-billing";

const paddle = client("PADDLE_SECRET", true);

// Will send the request to Sandbox:
cancelSubscription(paddle, "SUBCRIPTION_ID");

Typing custom_data

To add types to custom_data fields to Price, Product, SubscriptionItem, Subscription, Transaction, Customer, Address and Business add the generic argument to client:

const paddle = client<{
  Product: CustomDataProduct;
  Price: CustomDataPrice;
  Transaction: CustomDataTransaction;
  Subscription: CustomDataSubscription;
  SubscriptionItem: CustomDataSubscriptionItem;
  Customer: CustomDataCustomer;
  Address: CustomDataAddress;
  Business: CustomDataBusiness;
}>("PADDLE_SECRET");

From now on, all corresponding entities will have custom_data typed.

⚠️ When specifing Subscription and Transaction, you should make sure they overlap. Fields that do not overlap should be optional. It's dictated by the web's custom data-assigning to relevant transaction and subscription simultaneously. Creating an API or web client with incompatible custom data definitions will result in the client function returning never.

All custom data fields are optional so that you can type only selected entities:

const paddle = client<{
  Product: CustomDataProduct;
  Price: CustomDataPrice;
}>("PADDLE_SECRET");

Key function

You can also pass a function that returns the key as the key argument, which allows the use of Google Cloud Secrets and calling client on the module level where the secrets aren't defined in process:

const paddle = client(() => "PADDLE_SECRET");

Methods List

Webhooks

To verify and parse the Paddle webhook, use parseWebhookBody function:

import express from "express";
import { parseWebhookBody } from "paddle-billing/webhooks";

const app = express();

app.use(express.raw());

// Use the webhook's secret that you get when creating it the Paddle admin:
const secret = process.env.PADDLE_WEBHOOK_SECRET;

app.get("/paddle-webhook", (request, response) => {
  // Extract the webhook signature from the headers
  const signature = request.headers["paddle-signature"];
  if (!signature) {
    response.status(400).send("Bad Request");
    return;
  }

  // Parse the webhook
  const webhook = parseWebhookBody(
    null,
    secret,
    signature,
    // ⚠️ the body must be raw string to parse!
    request.body.toString()
  );

  // If the webhook is invalid, it will be null
  if (!webhook) {
    response.status(400).send("Bad Request");
    return;
  }

  response.send("OK");
});

If you have custom data types defined, pass the client as the first argument so that the custom data is correctly inferred:

const paddle = client<{
  Product: CustomDataProduct;
  Price: CustomDataPrice;
  Transaction: CustomDataTransaction;
  Subscription: CustomDataSubscription;
  SubscriptionItem: CustomDataSubscriptionItem;
  Customer: CustomDataCustomer;
  Address: CustomDataAddress;
  Business: CustomDataBusiness;
}>("PADDLE_SECRET");

// ...later:

const webhook = parseWebhookBody(
  paddle,
  secret,
  signature,
  request.body.toString()
);

Web

The package also provides the web portion of the Paddle Billing platform replacing the first-party package @paddle/paddle-js. Unlike the official package, this one provides more elaborate types and integration with custom data that you might use on the backend.

To load the web API (known as Paddle.js), use loadScript:

import { loadScript } from "paddle-billing/web";

loadScript().then((Paddle) => {
  Paddle.Checkout.open({
    settings: {
      displayMode: "overlay",
      theme: "light",
      locale: "en",
    },

    items: [
      {
        priceId: "pri_01gm81eqze2vmmvhpjg13bfeqg",
        quantity: 1,
      },
      {
        priceId: "pri_01gm82kny0ad1tk358gxmsq87m",
        quantity: 1,
      },
    ],
  });
});

If you have custom data assigned to Paddle entities, use the loadScript generic param, the same way as when creating the API client:

interface CustomData {
  Transaction: AccountData;
  Subscription: AccountData;
}

interface AccountData {
  accountId: string;
}

const paddle = client<CustomData>("PADDLE_SECRET");

// ...later on web:

loadScript<CustomData>().then((Paddle) => {
  Paddle.Checkout.open({
    items: [
      {
        priceId: "pri_01gm81eqze2vmmvhpjg13bfeqg",
        quantity: 1,
      },
    ],

    customData: {
      accountId: "ACCOUNT_ID",
    },
  });
});

⚠️ When specifing Subscription and Transaction, you should make sure they overlap. Fields that do not overlap should be optional. It's dictated by the web's custom data-assigning to relevant transaction and subscription simultaneously. Creating an API or web client with incompatible custom data definitions will result in the client function returning never.

Read more about using custom data through API.

License

MIT © Sasha Koss

About

Paddle Billing API and Webhooks wrapper with detailed TypeScript types

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

 

Packages

No packages published

Languages