@blocklet/payment-react
Version:
Reusable react components for payment kit v2
197 lines (159 loc) • 9.02 kB
Markdown
# OverdueInvoicePayment
The `OverdueInvoicePayment` component is a specialized tool designed to handle the payment of overdue invoices for a specific customer or subscription. It simplifies the process by automatically fetching overdue invoices and presenting users with a clear interface to settle their outstanding payments.
This component can operate in two modes: a default mode that displays a pre-built dialog for quick integration, and a custom mode that provides the flexibility to build a unique user interface using a render prop.
## How It Works
The component orchestrates the entire overdue payment process, from fetching data to handling the final transaction confirmation.
```d2 Overdue Payment Flow
direction: down
User: {
shape: c4-person
}
Client-App: {
label: "Client Application"
shape: rectangle
OverdueInvoicePayment-Component: {
label: "OverdueInvoicePayment Component"
shape: rectangle
}
}
Payment-Backend: {
label: "Payment Backend"
shape: cylinder
}
DID-Wallet: {
label: "DID Wallet"
icon: "https://www.arcblock.io/image-bin/uploads/37198ddc4a0b9e91e5c1c821ab895a34.svg"
}
User -> Client-App.OverdueInvoicePayment-Component: "1. Renders the component"
Client-App.OverdueInvoicePayment-Component -> Payment-Backend: "2. Fetch overdue invoices"
Payment-Backend -> Client-App.OverdueInvoicePayment-Component: "3. Return invoice summary"
Client-App.OverdueInvoicePayment-Component -> User: "4. Display payment dialog"
User -> Client-App.OverdueInvoicePayment-Component: "5. Clicks 'Pay Now'"
Client-App.OverdueInvoicePayment-Component -> DID-Wallet: "6. Opens connect session (collect-batch)"
User -> DID-Wallet: "7. Approves payment"
DID-Wallet -> Client-App.OverdueInvoicePayment-Component: "8. Sends success callback"
Payment-Backend -> Client-App.OverdueInvoicePayment-Component: "9. WebSocket event (invoice.paid)"
Client-App.OverdueInvoicePayment-Component -> User: "10. Update UI (e.g., close dialog)"
```
## Props
The `OverdueInvoicePayment` component accepts the following props to customize its behavior:
| Prop | Type | Description |
|---|---|---|
| `subscriptionId` | `string` | The ID of the subscription to check for overdue invoices. Either `subscriptionId` or `customerId` must be provided. |
| `customerId` | `string` | The ID or DID of the customer. Use this to handle all overdue invoices for a customer. |
| `mode` | `'default'` \| `'custom'` | The rendering mode. `'default'` shows a pre-built dialog. `'custom'` uses the `children` render prop for a custom UI. Defaults to `'default'`. |
| `onPaid` | `(id, currencyId, type) => void` | An optional callback function that is triggered after payment is successfully completed. `id` will be the `subscriptionId` or `customerId`, `type` will be `'subscription'` or `'customer'`. |
| `dialogProps` | `object` | Optional props to pass to the underlying Material-UI `Dialog` component in `default` mode. e.g., `{ open: true, title: 'Custom Title', onClose: handleClose }`. |
| `detailLinkOptions` | `object` | Optional settings for the "View Details" link. Can be used to disable the link, change its text, or provide a custom `onClick` handler. Format: `{ enabled?: boolean, onClick?: function, title?: string }`. |
| `successToast` | `boolean` | If `true`, a success toast notification is shown upon successful payment. Defaults to `true`. |
| `alertMessage` | `string` | An optional message to append to the default title text when in customer mode. |
| `children` | `(handlePay, data) => React.ReactNode` | A render prop function used only when `mode` is `'custom'`. It receives a `handlePay` function and a `data` object. |
| `authToken` | `string` | An optional authentication token for API requests, useful for server-to-server or cross-origin scenarios. |
### `children` Render Prop
When using `mode="custom"`, the `children` function receives two arguments:
1. **`handlePay(item: SummaryItem)`**: A function to initiate the payment process for a specific currency group. The `item` object comes from the `data.summary` object.
2. **`data`**: An object containing the fetched payment information:
* `subscription?: Subscription`: The subscription details, if `subscriptionId` was provided.
* `summary: { [key: string]: SummaryItem }`: An object where each key is a currency ID and the value contains the total amount, currency details, and payment method.
* `invoices: Invoice[]`: An array of all overdue invoice objects.
* `subscriptionCount?: number`: The number of subscriptions with overdue invoices (for customer mode).
* `detailUrl: string`: The URL to view detailed invoice information.
## Usage Examples
All examples assume you have `PaymentProvider` set up in your application.
### 1. Default Mode for a Subscription
This is the simplest way to handle overdue payments for a specific subscription. The component will automatically render a dialog if any overdue invoices are found.
```tsx SubscriptionOverdue.tsx icon=logos:react
import { OverdueInvoicePayment, PaymentProvider } from '@blocklet/payment-react';
import { useSessionContext } from './hooks/session'; // Your custom session hook
function SubscriptionPage({ subscriptionId }) {
const { session, connect } = useSessionContext();
const handlePaymentSuccess = (id, currencyId, type) => {
console.log(`Payment successful for ${type} ${id} with currency ${currencyId}`);
// You can refetch subscription data here to update its status
};
return (
<PaymentProvider session={session} connect={connect}>
{/* This component will be null if there are no overdue invoices */}
<OverdueInvoicePayment
subscriptionId={subscriptionId}
onPaid={handlePaymentSuccess}
/>
{/* Other subscription details can be rendered here */}
</PaymentProvider>
);
}
```
### 2. Default Mode for a Customer
Use this to create a centralized place for a customer to pay all their overdue invoices across multiple subscriptions.
```tsx CustomerDashboard.tsx icon=logos:react
import { OverdueInvoicePayment, PaymentProvider } from '@blocklet/payment-react';
import { useSessionContext } from './hooks/session'; // Your custom session hook
function CustomerDashboard() {
const { session, connect } = useSessionContext();
return (
<PaymentProvider session={session} connect={connect}>
<h2>Payment Center</h2>
<p>Please settle any outstanding payments to ensure uninterrupted service.</p>
<OverdueInvoicePayment
customerId={session.user.did}
onPaid={() => {
console.log('All customer overdue invoices paid for a currency!');
// Refresh customer account status
}}
/>
{/* The rest of the customer dashboard */}
</PaymentProvider>
);
}
```
### 3. Custom UI Mode
For full control over the user experience, use `mode="custom"`. This allows you to integrate the payment functionality directly into your existing UI instead of using a dialog.
```tsx CustomOverdueUI.tsx icon=logos:react
import { OverdueInvoicePayment, PaymentProvider, Amount } from '@blocklet/payment-react';
import { useSessionContext } from './hooks/session'; // Your custom session hook
import { Card, CardContent, Typography, Button, Stack } from '@mui/material';
function CustomOverdueUI({ subscriptionId }) {
const { session, connect } = useSessionContext();
return (
<PaymentProvider session={session} connect={connect}>
<OverdueInvoicePayment
subscriptionId={subscriptionId}
mode="custom"
onPaid={() => console.log('Custom UI payment successful!')}
>
{(handlePay, { summary, invoices }) => {
const summaryList = Object.values(summary);
if (invoices.length === 0) {
return <Typography>No overdue payments. All clear!</Typography>;
}
return (
<Card variant="outlined">
<CardContent>
<Typography variant="h6" color="error" gutterBottom>
You have {invoices.length} overdue invoice(s).
</Typography>
<Stack spacing={2} mt={2}>
{summaryList.map((item) => (
<Stack key={item.currency.id} direction="row" justifyContent="space-between" alignItems="center">
<Typography>
Total Due: <Amount amount={item.amount} decimal={item.currency.decimal} symbol={item.currency.symbol} />
</Typography>
<Button
variant="contained"
color="primary"
onClick={() => handlePay(item)}
>
Pay with {item.currency.symbol}
</Button>
</Stack>
))}
</Stack>
</CardContent>
</Card>
);
}}
</OverdueInvoicePayment>
</PaymentProvider>
);
}
```