Get Products Prices From Paddle in React App with TypeScript

June 9, 2022

3 min read

Get Products Prices From Paddle in React App with TypeScript
Watch on YouTube

After the free trial, we ask a user to buy a subscription. To get monthly and yearly prices, I have useMembershipPricesQuery. First, we want to get Paddle SDK. I have a post about loading third-party scripts - you can reade it here.

useMembershipPricesQuery
import { MembershipPeriod } from "membership"
import { usePaddleSdk } from "membership/paddle/hooks/usePaddleSdk"
import { PaddleProductCode } from "membership/paddle/PaddleProductCode"
import { PaddleSdk, PaddleSdkProductPrice } from "membership/paddle/PaddleSdk"
import { ProductPlanPrice } from "membership/ProductPlanPrice"
import { useQuery } from "react-query"

const getPaddleProductPrice = async (
  Paddle: PaddleSdk,
  product: number
): Promise<ProductPlanPrice> => {
  const { price }: PaddleSdkProductPrice = await new Promise(resolve =>
    Paddle.Product.Prices(product, resolve)
  )

  const { gross } = price

  const index = gross.search(/\d/)
  const currency = gross.slice(0, index)
  const amountAsString = gross.slice(index).replace(",", "")
  const amount = Number(amountAsString)

  return { currency, amount }
}

export const membershipPricesQueryKey = "membershipPrices"

export const useMembershipPricesQuery = () => {
  const { data: paddleSdk } = usePaddleSdk()

  return useQuery(
    membershipPricesQueryKey,
    async () => {
      const [monthly, yearly] = await Promise.all(
        [PaddleProductCode.monthly, PaddleProductCode.yearly].map(product =>
          getPaddleProductPrice(paddleSdk as PaddleSdk, product)
        )
      )

      return {
        monthly,
        yearly,
      } as Record<MembershipPeriod, ProductPlanPrice>
    },
    {
      keepPreviousData: true,
      refetchOnWindowFocus: false,
      refetchOnMount: false,
      refetchOnReconnect: false,
      staleTime: Infinity,
      enabled: !!paddleSdk,
    }
  )
}

Once we have the SDK, we can use it to get prices for monthly and yearly subscriptions. We iterate over a record with product codes and call getPaddleProductPrice. Since Paddle doesn't have type definitions, I fill up the PaddleSdk interface as I go. We call the prices method to get the product price and wrap the call with a promise since Paddle uses callbacks.

Paddle.ts
export interface PaddleSdkProductPrice {
  price: {
    gross: string
  }
}

interface PaddleSdkSetupParams {
  vendor: number
}

export interface CheckoutSuccessInfo {
  checkout: {
    id: string
  }
  product: {
    id: number
  }
  user: {
    id: string
  }
}

interface PaddleSdkOpenCheckoutParams {
  method: string
  product: number
  allowQuantity: boolean
  disableLogout: boolean
  frameTarget: string
  successCallback: (info: CheckoutSuccessInfo) => void
  closeCallback: () => void
  frameInitialHeight: number
  loadCallback: () => void
  email?: string | null
  passthrough: string
  override: any
  frameStyle: string
}

export interface PaddleSdk {
  Product: {
    Prices: (
      code: number,
      resolve: (prices: PaddleSdkProductPrice) => void
    ) => void
  }
  Setup: (params: PaddleSdkSetupParams) => void
  Checkout: {
    open: (params: PaddleSdkOpenCheckoutParams) => void
  }
  Environment: {
    set: (mode: "sandbox") => void
  }
  Order: {
    details: (checkoutId: string, callback: (info: any) => void) => void
  }
}

We use a regular expression to separate a number from a currency name. Since Paddle formats big numbers with commas, we need to remove them.