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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@blocklet/payment-react

v1.22.32

Published

Reusable react components for payment kit v2

Readme

@blocklet/payment-react

npm version

A React component library for building payment flows, subscriptions, and donation systems in blocklets, seamlessly integrated with Payment Kit.

Features

  • 🛠️ Pre-built UI Components: Includes checkout forms, pricing tables, donation widgets, and more
  • 🎨 Customizable Themes: Full control over styling via Material-UI themes
  • 🌍 i18n Support: Built-in localization for global audiences
  • 🧩 Lazy Loading: Optimize bundle size with dynamic imports
  • 💳 Payment Operations: Handle subscriptions, refunds, invoices, and metered billing

Related Links

Installation

npm install @blocklet/payment-react 

Quick Start

Basic Integration

import { PaymentProvider, CheckoutForm } from '@blocklet/payment-react';

function App() {
  return (
    <PaymentProvider session={session} connect={connectApi}>
      <CheckoutForm 
        id="plink_xxx" // Payment Link ID
        mode="inline"  // Embed directly in your UI
        showCheckoutSummary={true}
        onChange={(state) => console.log('Checkout State:', state)}
      />
    </PaymentProvider>
  );
}

Available Components & Utilities

Core Components

  • CheckoutForm - Payment form for checkout sessions and payment links
  • CheckoutTable - Pricing table display
  • CheckoutDonate - Donation widget
  • OverdueInvoicePayment - Handle overdue invoice payments
  • AutoTopup - Auto-recharge configuration display card
  • AutoTopupModal - Auto-recharge configuration modal

Form Components

  • FormInput - Base form input component
  • PhoneInput - Phone number input with validation
  • AddressForm - Complete address form
  • StripeForm - Stripe payment form
  • CurrencySelector - Currency selection dropdown
  • CountrySelect - Country selection dropdown

Display Components

  • Status - Status indicator
  • Livemode - Test mode indicator
  • Switch - Toggle switch
  • ConfirmDialog - Confirmation dialog
  • Amount - Amount display with formatting
  • TruncatedText - Text truncation
  • Link - Safe navigation link

UI Components

// Loading Button with state management
import { LoadingButton } from '@blocklet/payment-react';

function PaymentButton() {
  const [loading, setLoading] = useState(false);
  
  const handlePayment = async () => {
    setLoading(true);
    try {
      await processPayment();
    } finally {
      setLoading(false);
    }
  };

  return (
    <LoadingButton
      loading={loading}
      onClick={handlePayment}
      variant="contained"
      color="primary"
    >
      Pay Now
    </LoadingButton>
  );
}

Transaction Components

  • TxLink - Transaction link
  • TxGas - Gas fee display
  • PaymentBeneficiaries - Payment beneficiaries list

History Components

  • CustomerInvoiceList - Invoice history list
  • CustomerPaymentList - Payment history list

Context Providers

  • PaymentProvider - Payment context provider
  • DonateProvider - Donation context provider
  • PaymentThemeProvider - Theme provider

Hooks

  • useSubscription - event socket callback
  • useMobile - Mobile detection

Utilities

API Client

import { api } from '@blocklet/payment-react';

// Basic usage
const response = await api.get('/api/payments');
const data = await api.post('/api/checkout', { amount: 100 });

// With query parameters
const results = await api.get('/api/invoices', { 
  params: { status: 'paid' } 
});

// With request config
const config = { 
  headers: { 'Custom-Header': 'value' }
};
const response = await api.put('/api/subscription', data, config);

Cached Request

import { CachedRequest } from '@blocklet/payment-react';

// Create a cached request
const priceRequest = new CachedRequest(
  'product-prices', 
  () => api.get('/api/prices'),
  {
    strategy: 'session',  // 'session' | 'local' | 'memory'
    ttl: 5 * 60 * 1000   // Cache for 5 minutes
  }
);

// Use the cached request
async function fetchPrices() {
  // Will use cache if available and not expired
  const prices = await priceRequest.fetch();
  
  // Force refresh cache
  const freshPrices = await priceRequest.fetch(true);
  
  return prices;
}

Date Handling

import { dayjs } from '@blocklet/payment-react';

// Format dates
const formatted = dayjs().format('YYYY-MM-DD');

// Parse timestamps
const date = dayjs(timestamp);
const unix = date.unix();

// Relative time
const relative = dayjs().from(date);

i18n Setup

// use your own translator
import { createTranslator } from '@blocklet/payment-react';

const translator = createTranslator({
  en: { 
    checkout: { title: 'Complete Payment' }
  },
  zh: { 
    checkout: { title: '完成支付' }
  }
});

// use payment-react locales
import { translations as extraTranslations } from '@blocklet/payment-react';
import merge from 'lodash/merge';

import en from './en';
import zh from './zh';

export const translations = merge(
  {
    zh,
    en,
  },
  extraTranslations
);

Lazy Loading

import { createLazyComponent } from '@blocklet/payment-react';

const LazyComponent = createLazyComponent(async () => {
  const [{ Component }, { useHook }] = await Promise.all([
    import('./Component'),
    import('./hooks')
  ]);
  
  globalThis.__DEPENDENCIES__ = { useHook };
  return Component;
});

Auto-Topup Components

The auto-topup feature allows users to automatically recharge their credit balance when it falls below a specified threshold. This helps ensure uninterrupted service usage.

AutoTopup

Display and manage auto-recharge configurations with different rendering modes.

import { AutoTopup, PaymentProvider } from '@blocklet/payment-react';

function CreditManagementPage() {
  return (
    <PaymentProvider session={session}>
      {/* Default mode - fully expanded display */}
      <AutoTopup
        currencyId="credit-currency-id"
        onConfigChange={(config) => {
          console.log('Auto-topup config updated:', config);
        }}
      />

      {/* Simple mode - collapsed by default, expandable */}
      <AutoTopup
        currencyId="credit-currency-id"
        mode="simple"
        onConfigChange={(config) => {
          // Handle configuration changes
          refreshCreditBalance();
        }}
      />

      {/* Custom mode - full control over rendering */}
      <AutoTopup
        currencyId="credit-currency-id"
        mode="custom"
        onConfigChange={(config) => console.log('Config updated:', config)}
      >
        {(openModal, config, paymentData, loading) => (
          <div>
            {loading ? (
              <div>Loading auto-topup configuration...</div>
            ) : (
              <div>
                <h3>Auto Recharge Status</h3>
                <p>Status: {config?.enabled ? 'Active' : 'Inactive'}</p>
                {config?.enabled && (
                  <p>
                    Threshold: {config.threshold} {config.currency?.symbol}
                  </p>
                )}
                {paymentData?.balanceInfo && (
                  <p>
                    Wallet Balance: {paymentData.balanceInfo.token} {config?.rechargeCurrency?.symbol}
                  </p>
                )}
                <button onClick={openModal}>Configure Auto-Topup</button>
              </div>
            )}
          </div>
        )}
      </AutoTopup>
    </PaymentProvider>
  );
}

AutoTopupModal

Configure auto-recharge settings including threshold, payment method, and purchase amount.

import { AutoTopupModal, PaymentProvider } from '@blocklet/payment-react';
import { useState } from 'react';

function AutoRechargeSettings({ currencyId }) {
  const [modalOpen, setModalOpen] = useState(false);

  const handleSuccess = (config) => {
    console.log('Auto-recharge configured:', config);
    setModalOpen(false);
    // Refresh the parent component or update state
  };

  const handleError = (error) => {
    console.error('Configuration failed:', error);
  };

  return (
    <PaymentProvider session={session}>
      <button onClick={() => setModalOpen(true)}>
        Setup Auto-Recharge
      </button>

      <AutoTopupModal
        open={modalOpen}
        onClose={() => setModalOpen(false)}
        currencyId={currencyId}
        onSuccess={handleSuccess}
        onError={handleError}
        defaultEnabled={true} // Start with auto-recharge enabled
      />
    </PaymentProvider>
  );
}

Component Props

AutoTopup Props:

  • currencyId [Required] - The currency ID for auto-recharge
  • onConfigChange [Optional] - Callback when configuration changes: (config: AutoRechargeConfig) => void
  • mode [Optional] - Rendering mode: 'default' | 'simple' | 'custom'
  • sx [Optional] - Custom styles
  • children [Optional] - Custom render function for custom mode: (openModal, config, paymentData, loading) => ReactNode

AutoTopupModal Props:

  • open [Required] - Whether modal is open
  • onClose [Required] - Close modal callback
  • currencyId [Required] - The currency ID for auto-recharge
  • customerId [Optional] - Customer ID (defaults to current session user)
  • onSuccess [Optional] - Success callback: (config: AutoRechargeConfig) => void
  • onError [Optional] - Error callback: (error: any) => void
  • defaultEnabled [Optional] - Whether to default the enabled state to true

Complete Examples

Donation Page Example

import { 
  DonateProvider, 
  CheckoutDonate,
  PaymentProvider 
} from '@blocklet/payment-react';
import { useEffect, useState } from 'react';

function DonationPage() {
  const [session, setSession] = useState(null);

  useEffect(() => {
    // Get session from your auth system
    const getSession = async () => {
      const userSession = await fetchSession();
      setSession(userSession);
    };
    getSession();
  }, []);

  return (
    <PaymentProvider session={session} connect={connectApi}>
      <DonateProvider 
        mountLocation="your-unique-donate-instance"
        description="Help locate this donation instance"
        defaultSettings={{
          btnText: 'Like',
        }}
      >
        <CheckoutDonate
          settings={{
            target: "post-123", // required, unique identifier for the donation instance
            title: "Support Author", // required, title of the donation modal
            description: "If you find this article helpful, feel free to buy me a coffee", // required, description of the donation 
            reference: "https://your-site.com/posts/123", // required, reference link of the donation 
            beneficiaries: [
              {
                address: "tip user did", // required, address of the beneficiary
                share: "100",  // required, percentage share
              },
            ],
          }}
        />

        {/* Custom donation history display */}
        <CheckoutDonate
          mode="custom"
          settings={{
            target: "post-123", // required, unique identifier for the donation instance
            title: "Support Author", // required, title of the donation modal
            description: "If you find this article helpful, feel free to buy me a coffee", // required, description of the donation 
            reference: "https://your-site.com/posts/123", // required, reference link of the donation 
            beneficiaries: [
              {
                address: "tip user did", // required, address of the beneficiary
                share: "100",  // required, percentage share
              },
            ],
          }}
        >
          {(openDonate, totalAmount, supporters, loading, settings) => (
            <div>
              <h2>Our Supporters</h2>
              {loading ? (
                <CircularProgress />
              ) : (
                <div>
                  <div>
                    Total Donations: {totalAmount} {supporters.currency?.symbol}
                  </div>
                  <div>
                    {supporters.supporters.map(supporter => (
                      <div key={supporter.id}>
                        <span>{supporter.customer?.name}</span>
                        <span>{supporter.amount_total} {supporters.currency?.symbol}</span>
                      </div>
                    ))}
                  </div>
                </div>
              )}
            </div>
          )}
        </CheckoutDonate>
      </DonateProvider>
    </PaymentProvider>
  );
}

Auto-Topup Integration Example

Complete example showing how to integrate auto-topup functionality into a credit management dashboard.

import {
  PaymentProvider,
  AutoTopup,
  AutoTopupModal,
  CustomerInvoiceList,
  Amount
} from '@blocklet/payment-react';
import { useState, useEffect } from 'react';
import { Grid, Card, CardContent, Typography, Button } from '@mui/material';

function CreditDashboard() {
  const [session, setSession] = useState(null);
  const [creditCurrencies, setCreditCurrencies] = useState([]);
  const [showSetupModal, setShowSetupModal] = useState(false);
  const [selectedCurrency, setSelectedCurrency] = useState(null);

  useEffect(() => {
    // Initialize session and fetch credit currencies
    const initializeDashboard = async () => {
      const userSession = await fetchSession();
      setSession(userSession);
      
      const currencies = await fetchCreditCurrencies();
      setCreditCurrencies(currencies);
    };
    
    initializeDashboard();
  }, []);

  const handleAutoTopupChange = (currencyId, config) => {
    console.log(`Auto-topup updated for ${currencyId}:`, config);
    // Update local state or refetch data
  };

  return (
    <PaymentProvider session={session}>
      <Grid container spacing={3}>
        {/* Credit Balance Cards with Auto-Topup */}
        {creditCurrencies.map((currency) => (
          <Grid item xs={12} md={6} key={currency.id}>
            <Card>
              <CardContent>
                <Typography variant="h6" gutterBottom>
                  {currency.name} Balance
                </Typography>
                
                {/* Current balance display */}
                <Typography variant="h4" color="primary" gutterBottom>
                  <Amount
                    amount={currency.balance}
                    decimal={currency.decimal}
                    symbol={currency.symbol}
                  />
                </Typography>
                
                {/* Auto-topup configuration */}
                <AutoTopup
                  currencyId={currency.id}
                  mode="simple"
                  onConfigChange={(config) => 
                    handleAutoTopupChange(currency.id, config)
                  }
                  sx={{ mt: 2 }}
                />
                
                {/* Manual setup button for currencies without auto-topup */}
                <Button
                  variant="outlined"
                  onClick={() => {
                    setSelectedCurrency(currency);
                    setShowSetupModal(true);
                  }}
                  sx={{ mt: 1 }}
                >
                  Configure Auto-Recharge
                </Button>
              </CardContent>
            </Card>
          </Grid>
        ))}

        {/* Usage History */}
        <Grid item xs={12}>
          <Card>
            <CardContent>
              <Typography variant="h6" gutterBottom>
                Credit Usage History
              </Typography>
              <CustomerInvoiceList
                customer_id={session?.user?.did}
                type="table"
                include_staking
                status="paid,open"
              />
            </CardContent>
          </Card>
        </Grid>
      </Grid>

      {/* Auto-topup Setup Modal */}
      {showSetupModal && selectedCurrency && (
        <AutoTopupModal
          open={showSetupModal}
          onClose={() => {
            setShowSetupModal(false);
            setSelectedCurrency(null);
          }}
          currencyId={selectedCurrency.id}
          onSuccess={(config) => {
            console.log('Auto-topup configured:', config);
            handleAutoTopupChange(selectedCurrency.id, config);
            setShowSetupModal(false);
            setSelectedCurrency(null);
          }}
          onError={(error) => {
            console.error('Auto-topup setup failed:', error);
          }}
          defaultEnabled={true}
        />
      )}
    </PaymentProvider>
  );
}

// Custom auto-topup display using custom mode
function CustomAutoTopupDisplay({ currencyId }) {
  return (
    <AutoTopup
      currencyId={currencyId}
      mode="custom"
    >
      {(openModal, config, paymentData, loading) => {
        if (loading) return <div>Loading...</div>;

        return (
          <Card variant="outlined">
            <CardContent>
              <Typography variant="subtitle1" gutterBottom>
                Smart Auto-Recharge
              </Typography>
              
              {config?.enabled ? (
                <div>
                  <Typography color="success.main" gutterBottom>
                    ✓ Active - Recharges when balance drops below {config.threshold} {config.currency?.symbol}
                  </Typography>
                  
                  <Typography variant="body2" color="text.secondary">
                    Next recharge: {config.quantity}x {config.price?.product?.name}
                  </Typography>
                  
                  {paymentData?.balanceInfo && (
                    <Typography variant="body2" sx={{ mt: 1 }}>
                      Wallet Balance: {paymentData.balanceInfo.token} {config.rechargeCurrency?.symbol}
                    </Typography>
                  )}
                </div>
              ) : (
                <Typography color="text.secondary" gutterBottom>
                  Auto-recharge is not configured
                </Typography>
              )}
              
              <Button
                variant="contained"
                size="small"
                onClick={openModal}
                sx={{ mt: 2 }}
              >
                {config?.enabled ? 'Modify Settings' : 'Setup Auto-Recharge'}
              </Button>
            </CardContent>
          </Card>
        );
      }}
    </AutoTopup>
  );
}

Subscription Management Example

  • ResumeSubscription component
    • Resume subscription, with support for re-stake if needed
    • Props:
      • subscriptionId: [Required] The subscription ID to resume
      • onResumed: [Optional] Callback function called after successful resume, receives (subscription)
      • dialogProps: [Optional] Dialog properties, default is { open: true }
      • successToast: [Optional] Whether to show success toast, default is true
      • authToken: [Optional] Authentication token for API requests
import {
  PaymentProvider,
  ResumeSubscription,
  CustomerInvoiceList,
  Amount
} from '@blocklet/payment-react';

function SubscriptionPage({ subscriptionId }) {
  return (
    <PaymentProvider session={session}>
      <ResumeSubscription
        subscriptionId={subscriptionId}
        onResumed={(subscription) => {
          // Refresh subscription status
          refetchSubscription();
        }}
      />

      {/* Custom dialog props */}
      <ResumeSubscription
        subscriptionId={subscriptionId}
        dialogProps={{
          open: true,
          title: 'Resume Your Subscription',
          onClose: () => {
            // Handle dialog close
          }
        }}
      />

      {/* With auth token */}
      <ResumeSubscription
        subscriptionId={subscriptionId}
        authToken="your-auth-token"
      />
    </PaymentProvider>
  );
}
  • OverdueInvoicePayment component
    • Display overdue invoices for a subscription, and support batch payment

    • Props:

      • subscriptionId: [Optional] The subscription ID
      • customerId: [Optional] The customer ID or DID
      • onPaid: [Optional] Callback function called after successful payment, receives (id, currencyId, type)
      • mode: [Optional] Component mode, default or custom (default is default)
      • dialogProps: [Optional] Dialog properties, default is { open: true }
      • detailLinkOptions: [Optional] Detail link options, format: { enabled, onClick, title }
      • successToast: [Optional] Whether to show success toast, default is true
      • children: [Optional] Custom rendering function, used only when mode="custom"
    • Custom Mode:

      • children function receives two parameters:
        • handlePay: Function to start the payment process
        • data: Payment data (includes subscription, summary, invoices, subscriptionCount, detailUrl)
import {
  PaymentProvider,
  OverdueInvoicePayment,
  CustomerInvoiceList,
  Amount
} from '@blocklet/payment-react';

function SubscriptionPage({ subscriptionId }) {
  return (
    <PaymentProvider session={session}>
      {/* Handle subscription overdue payments */}
      <OverdueInvoicePayment
        subscriptionId={subscriptionId}
        onPaid={() => {
          // Refresh subscription status
          refetchSubscription();
        }}
      />
      {/* Handle customer overdue payment */}
      <OverdueInvoicePayment
        customerId={session.user.did}
        onPaid={() => {
          // Refresh customer status
          refetch();
        }}
      />

      {/* Custom Overdue Invoice Payment */}
      <OverdueInvoicePayment
        subscriptionId={subscriptionId}
        onPaid={() => {
          refetchSubscription();
        }}
        mode="custom"
      >
        {(handlePay, { subscription, summary, invoices }) => (
          <Card>
            <CardHeader title="Overdue Payments" />
            <CardContent>
              <Stack spacing={2}>
                {Object.entries(summary).map(([currencyId, info]) => (
                  <div key={currencyId}>
                    <Typography>
                      Due Amount: 
                      <Amount
                        amount={info.amount}
                      />
                      {info.currency?.symbol}
                    </Typography>
                    <Button
                      onClick={() => handlePay(info)}
                      variant="contained"
                    >
                      Pay Now
                    </Button>
                  </div>
                ))}
              </Stack>
            </CardContent>
          </Card>
        )}
      </OverdueInvoicePayment>

      {/* Display invoice history */}
      <CustomerInvoiceList
        subscription_id={subscriptionId}
        type="table"
        include_staking
        status="open,paid,uncollectible,void"
      />
    </PaymentProvider>
  );
}

Best Practices

Cache Management

// 1. Choose appropriate cache strategy
const shortLivedCache = new CachedRequest('key', fetchData, {
  strategy: 'memory',
  ttl: 60 * 1000 // 1 minute
});

const persistentCache = new CachedRequest('key', fetchData, {
  strategy: 'local',
  ttl: 24 * 60 * 60 * 1000 // 1 day
});

// 2. Clear cache when data changes
async function updateData() {
  await api.post('/api/data', newData);
  await cache.fetch(true); // Force refresh
}

// 3. Handle cache errors
try {
  const data = await cache.fetch();
} catch (err) {
  console.error('Cache error:', err);
  // Fallback to fresh data
  const fresh = await cache.fetch(true);
}

Bundle Optimization

  • Use lazy loading for non-critical components
  • Import only required components
  • Leverage code splitting with dynamic imports

Theme Consistency

  • Maintain consistent styling across components
  • Use theme provider for global style changes
  • Override styles at component level when needed

Theme Customization

Since version 1.14.22, the component includes a built-in theme provider. If you need to modify the styles of internal components, pass the theme property to override or inherit the external theme.

| Option | Description | | --- | --- | | default | Wrapped with built-in PaymentThemeProvider | | inherit | Use the parent component's themeProvider | | PaymentThemeOptions | Override some styles of PaymentThemeProvider |

// 1. Use themeOptions
<CheckoutForm
  id="plink_xxx"
  onChange={console.info}
  theme={{
    components: {
      MuiButton: {
        styleOverrides: {
          containedPrimary: {
            backgroundColor: '#1DC1C7',
            color: '#fff',
            '&:hover': {
              backgroundColor: 'rgb(20, 135, 139)',
            },
          },
        },
      },
    },
  }}
/>

// 2. Use theme sx
<CheckoutForm
  id="plink_xxx"
  showCheckoutSummary={false}
  onChange={console.info}
  theme={{
    sx: {
      '.cko-submit-button': {
        backgroundColor: '#1DC1C7',
        color: '#fff',
        '&:hover': {
          backgroundColor: 'rgb(20, 135, 139)',
        },
      },
    },
  }}
/>

Status & Utility Components

import { 
  Status,
  Livemode,
  Switch,
  Link,
  Amount
} from '@blocklet/payment-react';

// Status indicator for payment states
<Status 
  label="active"
  color="success"
  size="small"
  sx={{ margin: 1 }}
/>

// Test mode indicator
<Livemode />

// Custom switch button
<Switch
  checked={true}
  onChange={(checked) => console.log('Switched:', checked)}
/>

// Safe navigation link
<Link to="/demo" />


## License

Apache-2.0