-
Notifications
You must be signed in to change notification settings - Fork 141
Feat/add payment processing component #350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
5cc44d3
3a7a4ac
b9b0098
e406988
0a26bbd
807f9c0
7235834
617bb97
d8be30e
990fe34
02f2227
4025c8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| --- | ||
| title: Payment Processing | ||
| description: A full-screen payment processing state component that displays a loading spinner with secure transaction indicators and customizable messaging. | ||
| --- | ||
|
|
||
| <Tabs items={['Preview', 'Code']} className="bg-transparent border-none"> | ||
| <Tab value="Preview" className="border-none bg-transparent p-0 mt-3"> | ||
| <PreviewComponents registryName="payment-processing"> | ||
| <PaymentProcessingDemo /> | ||
| </PreviewComponents> | ||
| </Tab> | ||
|
|
||
| <Tab value="Code" className="mt-3"> | ||
| <include cwd lang="tsx" meta='title="src/components/payment-processing-demo.tsx"'> | ||
| src/components/payment-processing-demo.tsx | ||
| </include> | ||
| </Tab> | ||
| </Tabs> | ||
|
|
||
| ## Installation | ||
|
|
||
| <Tabs | ||
| items={["shadcn", "billingSDK"]} | ||
| defaultValue="shadcn" | ||
| className="border-none bg-transparent" | ||
| > | ||
| <Tab value="shadcn" className="mt-3 border-none bg-transparent p-0"> | ||
| <Tabs | ||
| items={["npx", "pnpm", "yarn", "bun"]} | ||
| defaultValue="npx" | ||
| groupId="installation-tabs" | ||
| > | ||
| <Tab value="npx"> | ||
| ```bash | ||
| npx shadcn@latest add @billingsdk/payment-processing | ||
| ``` | ||
| </Tab> | ||
| <Tab value="pnpm"> | ||
| ```bash | ||
| pnpm dlx shadcn@latest add @billingsdk/payment-processing | ||
| ``` | ||
| </Tab> | ||
| <Tab value="yarn"> | ||
| ```bash | ||
| yarn dlx shadcn@latest add @billingsdk/payment-processing | ||
| ``` | ||
| </Tab> | ||
| <Tab value="bun"> | ||
| ```bash | ||
| bunx shadcn@latest add @billingsdk/payment-processing | ||
| ``` | ||
| </Tab> | ||
| </Tabs> | ||
| </Tab> | ||
| <Tab value="billingSDK" className="mt-3 border-none bg-transparent p-0"> | ||
| <Tabs | ||
| items={["npx", "pnpm", "yarn", "bun"]} | ||
| defaultValue="npx" | ||
| groupId="billingsdk-cli-tabs" | ||
| > | ||
| <Tab value="npx"> | ||
| ```bash | ||
| npx @billingsdk/cli add payment-processing | ||
| ``` | ||
| </Tab> | ||
| <Tab value="pnpm"> | ||
| ```bash | ||
| pnpm dlx @billingsdk/cli add payment-processing | ||
| ``` | ||
| </Tab> | ||
| <Tab value="yarn"> | ||
| ```bash | ||
| yarn dlx @billingsdk/cli add payment-processing | ||
| ``` | ||
| </Tab> | ||
| <Tab value="bun"> | ||
| ```bash | ||
| bunx @billingsdk/cli add payment-processing | ||
| ``` | ||
| </Tab> | ||
| </Tabs> | ||
| </Tab> | ||
| </Tabs> | ||
|
|
||
| ## Usage | ||
|
|
||
| ```tsx | ||
| import PaymentProcessing from "@/components/billingsdk/payment-processing"; | ||
| ``` | ||
|
|
||
| ```tsx | ||
| // Basic usage with state control | ||
| const [isProcessing, setIsProcessing] = useState(false); | ||
|
|
||
| async function handlePayment() { | ||
| setIsProcessing(true); | ||
|
|
||
| try { | ||
| // Process payment | ||
| await processPaymentAPI(); | ||
| // Navigate to success page | ||
| } catch (error) { | ||
| // Handle error | ||
| } finally { | ||
| setIsProcessing(false); | ||
| } | ||
| } | ||
|
|
||
| <PaymentProcessing status={isProcessing} /> | ||
| ``` | ||
|
|
||
| ## Customization | ||
|
|
||
| ```tsx | ||
| import { CreditCard } from "lucide-react"; | ||
|
|
||
| <PaymentProcessing | ||
| status={true} | ||
| title="Processing Your Payment" | ||
| description="Securely connecting to payment gateway" | ||
| icon={CreditCard} | ||
| processLabel="256-bit Encryption Active" | ||
| warning="Do not refresh or close this tab" | ||
| /> | ||
| ``` | ||
|
|
||
| ## Props | ||
|
|
||
| | Prop | Type | Required | Description | | ||
| | -------------- | ------------- | -------- | -------------------------------------------------------------------- | | ||
| | `status` | `boolean` | ❌ | Controls visibility of the processing screen (default: `true`) | | ||
| | `title` | `string` | ❌ | Main heading text (default: `"Processing Payment"`) | | ||
| | `description` | `string` | ❌ | Subtitle text below the spinner (default: `"This may take a few moments"`) | | ||
| | `icon` | `LucideIcon` | ❌ | Icon component to display with the process label (default: `Shield`) | | ||
| | `processLabel` | `string` | ❌ | Security/process indicator text (default: `"Secure Transaction"`) | | ||
| | `warning` | `string` | ❌ | Warning message at the bottom (default: `"Please do not close this window"`) | | ||
|
|
||
| ## Features | ||
|
|
||
| - **Full-Screen Overlay**: Takes over the entire viewport to prevent user interaction during processing | ||
| - **Animated Spinner**: Rotating loader provides visual feedback that processing is ongoing | ||
| - **Security Indicators**: Displays trust signals like "Secure Transaction" with shield icon | ||
| - **Customizable Text**: All text elements can be customized to match your brand and context | ||
| - **Custom Icons**: Support for any Lucide React icon to match your payment provider or security messaging | ||
| - **Responsive Design**: Adapts gracefully to all screen sizes with proper spacing and typography | ||
|
|
||
| ## Use Cases | ||
|
|
||
| - **Payment Submission**: Display while processing credit card transactions | ||
| - **Subscription Activation**: Show during subscription setup or plan changes | ||
| - **Payment Method Verification**: Display while validating payment information | ||
| - **Refund Processing**: Use when processing refund requests | ||
| - **Checkout Flow**: Integrate as the final step after user confirms payment | ||
|
|
||
| ## Best Practices | ||
|
|
||
| 1. **Always Control State**: Use a state variable to control the `status` prop | ||
| 2. **Set Appropriate Timeouts**: Implement timeout handling for long-running processes | ||
| 3. **Error Handling**: Always have a fallback to hide the processing screen on errors | ||
| 4. **User Communication**: Update the description to reflect the current processing step | ||
| 5. **Accessibility**: Ensure the processing screen is announced to screen readers | ||
|
|
||
| ## Example | ||
|
|
||
| <include cwd lang="tsx" meta='title="src/components/payment-processing-demo.tsx"'> | ||
| src/components/payment-processing-demo.tsx | ||
| </include> | ||
|
|
||
| ## Theming | ||
|
|
||
| The payment processing component uses standard shadcn/ui theming with CSS variables. It adapts automatically to your configured light/dark modes and respects your custom color schemes. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| { | ||
| "$schema": "https://ui.shadcn.com/schema/registry-item.json", | ||
| "name": "payment-processing", | ||
| "title": "Payment Processing", | ||
| "description": "A payment processing state card with spinner and security messaging.", | ||
| "dependencies": ["lucide-react"], | ||
| "registryDependencies": ["card", "separator", "utils"], | ||
| "files": [ | ||
| { | ||
| "path": "src/registry/billingsdk/payment-processing.tsx", | ||
| "content": "import { Loader2, Shield, LucideIcon } from \"lucide-react\";\nimport { Separator } from \"@/components/ui/separator\";\n\ntype PaymentProcessingTypes = {\n status?: boolean;\n title?: string;\n description?: string;\n icon?: LucideIcon;\n processLabel?: string;\n warning?: string;\n};\n\nexport default function PaymentProcessing({\n status = true,\n title = \"Processing Payment\",\n description = \"This may take a few moments\",\n icon: Icon = Shield,\n processLabel = \"Secure Transaction\",\n warning = \"Please do not close this window\",\n}: PaymentProcessingTypes) {\n return (\n <>\n {status && (\n <div className=\"bg-background flex min-h-screen items-center justify-center p-4\">\n <div className=\"bg-card w-full max-w-sm space-y-6 rounded-xl border px-8 py-6 text-center md:p-8 lg:p-10\">\n <div className=\"space-y-4\">\n <div className=\"flex justify-center\">\n <Loader2 className=\"text-primary h-12 w-12 animate-spin\" />\n </div>\n <div className=\"space-y-2\">\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n {title}\n </h1>\n <p className=\"text-muted-foreground text-sm\">{description}</p>\n </div>\n </div>\n\n <Separator />\n\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-center gap-2 text-sm\">\n <Icon className=\"text-muted-foreground h-4 w-4\" />\n <span className=\"text-muted-foreground\">{processLabel}</span>\n </div>\n </div>\n\n <Separator />\n\n <p className=\"text-muted-foreground text-xs\">{warning}</p>\n </div>\n </div>\n )}\n </>\n );\n}\n", | ||
| "type": "registry:component", | ||
| "target": "components/billingsdk/payment-processing.tsx" | ||
| }, | ||
| { | ||
| "path": "src/registry/billingsdk/demo/payment-processing-demo.tsx", | ||
| "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport PaymentProcessing from \"@/registry/billingsdk/payment-processing\";\nimport { setTimeout } from \"timers\";\nimport { Button } from \"@/components/ui/button\";\n\nexport function PaymentProcessingDemo() {\n const [loading, setLoading] = useState(false);\n const handleProcessing = async () => {\n setTimeout(() => {\n setLoading(false);\n }, 8000);\n };\n\n const onClickHandler = async () => {\n setLoading(true);\n await handleProcessing();\n };\n\n return (\n <div className=\"bg-background flex min-h-screen items-center justify-center p-4\">\n <Button\n onClick={async () => await onClickHandler()}\n className={`${loading ? \"hidden\" : \"block\"}`}\n >\n Pay now!\n </Button>\n <PaymentProcessing status={loading} />\n </div>\n );\n}\n", | ||
| "type": "registry:component", | ||
| "target": "components/payment-processing-demo.tsx" | ||
| } | ||
| ], | ||
| "type": "registry:block" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,6 +48,7 @@ | |
| "@billingsdk/billing-settings", | ||
| "@billingsdk/proration-preview", | ||
| "@billingsdk/upcoming-charges", | ||
| "@billingsdk/payment-processing", | ||
| "@billingsdk/coupon", | ||
| "@billingsdk/limited-offer-dialog", | ||
| "@billingsdk/payment-failure", | ||
|
|
@@ -900,6 +901,26 @@ | |
| ], | ||
| "dependencies": ["lucide-react", "motion"], | ||
| "registryDependencies": ["button", "utils"] | ||
| }, | ||
| { | ||
| "name": "payment-processing", | ||
| "type": "registry:block", | ||
| "title": "Payment Processing", | ||
| "description": "A payment processing state card with spinner and security messaging.", | ||
| "files": [ | ||
| { | ||
| "path": "src/registry/billingsdk/payment-processing.tsx", | ||
| "type": "registry:component", | ||
| "target": "components/billingsdk/payment-processing.tsx" | ||
| }, | ||
| { | ||
| "path": "src/registry/billingsdk/demo/payment-processing-demo.tsx", | ||
| "type": "registry:component", | ||
| "target": "components/payment-processing-demo.tsx" | ||
| } | ||
| ], | ||
| "dependencies": ["lucide-react"], | ||
| "registryDependencies": ["card", "separator", "utils"] | ||
|
||
| } | ||
| ] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| "use client"; | ||
|
|
||
| import { useState } from "react"; | ||
| import PaymentProcessing from "@/registry/billingsdk/payment-processing"; | ||
|
||
| import { setTimeout } from "timers"; | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| import { Button } from "@/components/ui/button"; | ||
|
|
||
| export function PaymentProcessingDemo() { | ||
| const [loading, setLoading] = useState(false); | ||
| const handleProcessing = async () => { | ||
| setTimeout(() => { | ||
| setLoading(false); | ||
| }, 8000); | ||
| }; | ||
|
|
||
| const onClickHandler = async () => { | ||
| setLoading(true); | ||
| await handleProcessing(); | ||
| }; | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return ( | ||
| <div className="bg-background flex min-h-screen items-center justify-center p-4"> | ||
| <Button | ||
| onClick={async () => await onClickHandler()} | ||
| className={`${loading ? "hidden" : "block"}`} | ||
| > | ||
| Pay now! | ||
| </Button> | ||
| <PaymentProcessing status={loading} /> | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| "use client"; | ||
|
|
||
| import { useState } from "react"; | ||
| import PaymentProcessing from "@/registry/billingsdk/payment-processing"; | ||
| import { setTimeout } from "timers"; | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| import { Button } from "@/components/ui/button"; | ||
|
|
||
| export function PaymentProcessingDemo() { | ||
| const [loading, setLoading] = useState(false); | ||
| const handleProcessing = async () => { | ||
| setTimeout(() => { | ||
| setLoading(false); | ||
| }, 8000); | ||
| }; | ||
|
|
||
| const onClickHandler = async () => { | ||
| setLoading(true); | ||
| await handleProcessing(); | ||
| }; | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return ( | ||
| <div className="bg-background flex min-h-screen items-center justify-center p-4"> | ||
| <Button | ||
| onClick={async () => await onClickHandler()} | ||
| className={`${loading ? "hidden" : "block"}`} | ||
| > | ||
| Pay now! | ||
| </Button> | ||
| <PaymentProcessing status={loading} /> | ||
| </div> | ||
| ); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.