Overview
The Payout component provides a seamless integration with Stripe for collecting payment methods. It supports both credit/debit cards and SEPA direct debit, automatically handles 3D Secure authentication in a modal, and exposes methods to retrieve the payment method data.
This component requires a valid Stripe publishable API key. You must have a Stripe account and integrate with Stripe’s backend APIs to create setup intents.
Installation
Dependencies
The Payout component requires Stripe’s React libraries, which are included as dependencies:
@stripe/react-stripe-js
@stripe/stripe-js
Stripe Setup
- Create a Stripe account
- Get your publishable key from the Stripe Dashboard (starts with
pk_)
- Set up backend endpoints to create Stripe SetupIntents
Basic Usage
import { Payout } from '@adoptaunabuelo/react-components';
import { useRef } from 'react';
import type { PayoutRef } from '@adoptaunabuelo/react-components';
function MyComponent() {
const payoutRef = useRef<PayoutRef>(null);
const handleSubmit = async () => {
const paymentMethod = await payoutRef.current?.getPaymentMethod();
if (paymentMethod) {
console.log('Payment method ID:', paymentMethod.id);
// Send paymentMethod.id to your backend
}
};
return (
<div>
<Payout
ref={payoutRef}
stripeKey={process.env.REACT_APP_STRIPE_KEY}
paymentOption="card"
onLoading={(isLoading) => console.log('Loading:', isLoading)}
/>
<button onClick={handleSubmit}>Save Payment Method</button>
</div>
);
}
Props
Your Stripe publishable API key (starts with pk_). Get this from your Stripe Dashboard.stripeKey="pk_test_51H..."
paymentOption
'card' | 'sepa_debit'
required
Payment method type to collect:
"card" - Credit or debit card
"sepa_debit" - SEPA Direct Debit (European bank accounts)
3D Secure confirmation URL returned by Stripe when a setup intent requires additional authentication. When provided, automatically opens the confirmation modal.
Visual design variant for the payment form. Defaults to "primary".
Pre-filled user data for the payment form.{
email?: string; // Required for SEPA direct debit
}
Custom placeholder text for the cardholder name input.
Custom placeholder text for the email input (used in SEPA).
Whether to display the form in an error state.
Custom CSS styles for the card input form element.
Custom CSS styles for the main container.
Callback fired when 3D Secure authentication completes successfully.
Callback fired when the loading state changes (e.g., when processing payment method).(isLoading: boolean) => void
Ref Methods
The Payout component exposes methods via ref:
Collects and returns the payment method from Stripe.getPaymentMethod: () => Promise<PaymentMethod | undefined>
Returns a Stripe PaymentMethod object or undefined if collection fails.
Examples
Card Payment
import { Payout, PayoutRef } from '@adoptaunabuelo/react-components';
import { useRef, useState } from 'react';
function CardPaymentForm() {
const payoutRef = useRef<PayoutRef>(null);
const [loading, setLoading] = useState(false);
const handleSaveCard = async () => {
setLoading(true);
const paymentMethod = await payoutRef.current?.getPaymentMethod();
if (paymentMethod) {
// Send to your backend
await fetch('/api/save-payment-method', {
method: 'POST',
body: JSON.stringify({ paymentMethodId: paymentMethod.id })
});
}
setLoading(false);
};
return (
<div>
<h2>Add Payment Card</h2>
<Payout
ref={payoutRef}
stripeKey="pk_test_..."
paymentOption="card"
design="primary"
onLoading={setLoading}
/>
<button onClick={handleSaveCard} disabled={loading}>
{loading ? 'Processing...' : 'Save Card'}
</button>
</div>
);
}
SEPA Direct Debit
function SepaPaymentForm() {
const payoutRef = useRef<PayoutRef>(null);
const [userEmail] = useState('[email protected]');
const handleSaveAccount = async () => {
const paymentMethod = await payoutRef.current?.getPaymentMethod();
console.log('SEPA payment method:', paymentMethod);
};
return (
<div>
<h2>Add Bank Account</h2>
<Payout
ref={payoutRef}
stripeKey="pk_test_..."
paymentOption="sepa_debit"
userData={{ email: userEmail }}
placeholderEmail="Email address"
placeholderName="Account holder name"
onLoading={(loading) => console.log('Loading:', loading)}
/>
<button onClick={handleSaveAccount}>Save Account</button>
</div>
);
}
With 3D Secure Authentication
function SecurePaymentForm() {
const payoutRef = useRef<PayoutRef>(null);
const [confirmUrl, setConfirmUrl] = useState<string>();
const handleSubmit = async () => {
const paymentMethod = await payoutRef.current?.getPaymentMethod();
// Send to backend to create setup intent
const response = await fetch('/api/create-setup-intent', {
method: 'POST',
body: JSON.stringify({ paymentMethodId: paymentMethod?.id })
});
const data = await response.json();
// If 3DS required, Stripe returns a confirmation URL
if (data.requiresAction) {
setConfirmUrl(data.confirmUrl);
}
};
const handleSetupConfirmed = () => {
console.log('3D Secure authentication completed!');
setConfirmUrl(undefined);
// Continue with your flow
};
return (
<Payout
ref={payoutRef}
stripeKey="pk_test_..."
paymentOption="card"
stripeConfirmUrl={confirmUrl}
onSetupConfirmed={handleSetupConfirmed}
onLoading={(loading) => console.log('Loading:', loading)}
/>
);
}
With Error Handling
function PaymentFormWithErrors() {
const payoutRef = useRef<PayoutRef>(null);
const [hasError, setHasError] = useState(false);
const handleSubmit = async () => {
try {
const paymentMethod = await payoutRef.current?.getPaymentMethod();
if (!paymentMethod) {
setHasError(true);
return;
}
setHasError(false);
// Process payment method
} catch (error) {
console.error('Payment method error:', error);
setHasError(true);
}
};
return (
<div>
<Payout
ref={payoutRef}
stripeKey="pk_test_..."
paymentOption="card"
error={hasError}
design="primary"
/>
{hasError && (
<p style={{ color: 'red' }}>Please check your card details</p>
)}
<button onClick={handleSubmit}>Submit</button>
</div>
);
}
Integration Flow
Typical integration with Stripe involves these steps:
- Render Payout component with your Stripe publishable key
- User enters payment details in the Stripe-hosted form
- Call
getPaymentMethod() when ready to submit
- Send payment method ID to your backend
- Backend creates SetupIntent with Stripe API
- If 3DS required, backend returns confirmation URL
- Set
stripeConfirmUrl prop to trigger authentication modal
- User completes 3DS in the modal
onSetupConfirmed fires when authentication succeeds
- Continue your payment flow
3D Secure Flow
When 3D Secure authentication is required:
- Component displays confirmation URL in a modal (600×400px)
- User completes authentication in their bank’s interface
- Bank sends
3DS-authentication-complete message via window.postMessage
- Modal closes automatically
onSetupConfirmed callback is triggered
Styling
The component uses Stripe Elements with custom font styling:
- Font: Poppins (loaded from Google Fonts)
- Fully styled and themed by Stripe Elements
- Use
cardStyle prop for custom form styling
- Use
design prop to match your app’s design system
Notes
- Never expose your Stripe secret key in client-side code
- Always use your publishable key (starts with
pk_)
- Use test keys (
pk_test_...) during development
- Use live keys (
pk_live_...) only in production
- SEPA direct debit requires user email
- Card payments may require 3D Secure based on card issuer and amount
- The component automatically handles Stripe.js loading
- Payment method data never touches your servers (handled by Stripe)
- Component is forwardRef-enabled for ref access
Resources