@blocklet/payment-react
TypeScript icon, indicating that this package has built-in type declarations

1.19.9 • Public • Published

@blocklet/payment-react

npm version

A React component library for building payment flows, subscriptions, and donation systems in Blocklet applications. Seamlessly integrated with Blocklet's payment infrastructure.

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

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;
});

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>
  );
}

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

Readme

Keywords

Package Sidebar

Install

npm i @blocklet/payment-react

Weekly Downloads

761

Version

1.19.9

License

Apache-2.0

Unpacked Size

1.52 MB

Total Files

397

Last publish

Collaborators

  • wangshijun
  • gxw