npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@billing-js/react-billing-js

v1.1.34

Published

A powerful, low-code react component library to quickly integrate Stripe into your React app, with customizable pricing page and customer portal, support for shared subscriptions and much more.

Downloads

278

Readme

React Billing.js

Setup pricing page and a customer portal by copy pasting code snippets.

Install

Using NPM:

npm i @billing-js/react-billing-js

Using Yarn:

yarn add @billing-js/react-billing-js

Stripe account Id

Get your Billing.js Stripe account id on billingjs.com.

Billing.js is using Stripe Connect on the background to access your products and manage the subscriptions for you.

Documentation

Checkout the full Billing.js documentation on docs.billingjs.com.

Usage

Wrap your App with the BillingProvider component to give it access to the billing context.

index.tsx - Root component of your react app

import React from "react"
import { render } from "react-dom"
import { BillingProvider } from "react-billing-js"

import YourApp from "./YourApp"

const ReactApp = (
    // Wrap your app with the billing provider with your stripe account id
    <BillingProvider
        stripeAccount={"acct_..."} // Your stripe account id that you can find in the billing.js dashboard (https://billingjs.com)
        options={{ termsAndConditionsUrl: "/terms", productPageUrl: "/pricing" }} // Optional
        onError={(error) => {}} // Optional for error handling
    >
        <YourApp />
    </BillingProvider>
)

render(<ReactApp />, document.getElementById("root"))

Authentication

Set your router to require authentication when accessing the customer portal.

App.tsx - Root component

import { useAuth, CustomerPortal } from "react-billing-js"
import { Switch, Route, Redirect, BrowserRouter as Router } from "react-router-dom"

import Pricing from "./Pricing"

export default () => {

    // use the useAuth hook to get the current user
    const { loading, user, signOut } = useAuth()

    const renderPrivateRoute = (Component, redirect = "/login") => () => {
        if (user) {
            return React.isValidElement(Component) ? Component : <Component />
        }
        return <Redirect to={redirect} />
    }

    if (loading) {
        return <div>loading...</div>
    }

    <Router>
        <Switch>
            <Route exact={true} path="/subscription" render={renderPrivateRoute(CustomerPortal)} />
            <Route exact={true} path="/pricing" component={Pricing} />
            <Route exact={true} path="/login" component={Login} />
            <Redirect to="/pricing" />
        </Switch>
    </Router>
)

Fetch the HMAC signature from your server to authenticate the user on the Billing.js module.

Login.tsx - Login page component

import { useAuth } from "react-billing-js"
const { loading, user, signIn } = useAuth()

// fetch the hmac now or retrieve it from the the app state, then sign in the user
// follow the steps in the [billingjs.com](https://billingjs.com) documentation to get the HMAC
const onSignIn = () => fetch("/URL_TO_GET_THE_HMAC").then((res) => signIn({ hmac: res?.data?.hmac, email: user.email }))

if (loading) {
    return <div>loading...</div>
}

if (user) {
    return <div>Signed in!</div>
}

return (
    <div>
        <button onClick={onSignIn}>Sign in</button>
    </div>
)

Pricing page

Show the pricing of the products of your choices to the user.

Pricing.tsx - Pricing page component

import { useProducts, PaymentModal } from "react-billing-js"
import { useHistory } from "react-router-dom"

export default () => {
    const history = useHistory() // optional, if you are using react-router

    // Billing.js useProducts hook
    const {
        loading, // loading state
        products: [premiumPlan], // list of the products fetched
        activeSubscriptions, // list of active subscriptions of the user
        pricingFilter: {
            currency: {
                selectedCurrency, // current currency (USD by default)
                availableCurrencies, // list of the available currencies
                setCurrency // function to set the desired currency
            },
            recurrence: {
                selectedRecurrence, // current recurrence
                availableRecurrences, // list of the available recurrences
                setRecurrence // function to set the desired recurrence
            }
        }
    } = useProducts(["prod_..."], {
        modal: {
            maskClassName: "bg-white opacity-75" // modal mask class name
        },
        normalizePriceOnRecurrence: "monthly", // Normalize price on recurrence
        defaultCurrency: "usd", // Default currency
        defaultRecurrence: "Yearly", // Default recurrence
        signIn: () => history.push("/login"), // Function to sign in the user
        onPaymentSuccessful: () => console.log(`Payment successful`), // Callback when payment is successful
        onPaymentError: (error) => console.log(`Payment error`, error) // Callback when payment is unsuccessful
    })

    return (
        <div>
            {/*Payment modal that will open when a pricing is selected*/}
            <PaymentModal />

            {/*Plan description and price*/}
            <h3>{premiumPlan.name}</h3>
            <h2>
                {selectedCurrency.symbol} {premiumPlan.selectedPricing.priceInteger}.
                {premiumPlan.selectedPricing.priceDecimal}/{premiumPlan.selectedPricing.pricingRecurrence}
            </h2>
            {/* Payment button */}
            {loading ||
            activeSubscriptions.find(
                (subscription) => subscription.items?.data[0]?.price?.id === premiumPlan.selectedPricing.id
            ) ? ( // If the user has an active subscription to this product
                <button disabled={true}>{loading ? "Loading..." : "Current plan"}</button>
            ) : (
                // If the user does not have an active subscription to this product
                <button onClick={premiumPlan.selectedPricing.onSelectPricing}>
                    {
                        activeSubscriptions.length > 0
                            ? "Switch plan" // If the user has an active subscription to another product
                            : premiumPlan.selectedPricing?.recurring?.trial_period_days
                            ? "Start your trial" // If the product has a trial period
                            : "Get started" // If the product is billed immediately
                    }
                </button>
            )}
        </div>
    )
}