Skip to main content

usePaymentMethods

Overview

usePaymentMethods is a comprehensive React hook for managing Stripe payment methods. It provides full CRUD operations (create, read, update, delete) for payment methods, along with the ability to set a default payment method. The hook leverages TanStack React Query for caching, optimistic updates, and automatic cache invalidation on mutations.

Source: template/hooks/use-payment-methods.ts

Signature

function usePaymentMethods(): UsePaymentMethodsReturn

Parameters

This hook takes no parameters. It operates on the authenticated user's payment methods automatically.

Return Values

The hook returns an object with the following properties:

Data

PropertyTypeDescription
paymentMethodsPaymentMethodData[]Array of the user's payment methods. Defaults to [] if no data is loaded.
hasPaymentMethodsbooleanWhether the user has at least one payment method on file.

Status

PropertyTypeDescription
isLoadingbooleantrue while the initial payment methods fetch is in progress.
isErrorbooleantrue if the fetch failed.
isSuccessbooleantrue if the fetch completed successfully.
errorPaymentMethodError | nullThe error object if the fetch failed.

Actions

PropertyTypeDescription
refetch() => voidManually refetch payment methods from the API.
deletePaymentMethod(paymentMethodId: string) => voidDelete a payment method. Prompts the user with a confirmation dialog before proceeding.
isDeletingbooleantrue while a delete mutation is in progress.
createPaymentMethod(vars: { setupIntentId: string; setAsDefault?: boolean }) => voidCreate (attach) a new payment method using a SetupIntent ID. Fire-and-forget.
createPaymentMethodAsync(vars: { setupIntentId: string; setAsDefault?: boolean }) => Promise<PaymentMethodData>Async version of createPaymentMethod that returns a Promise.
isCreatingbooleantrue while a create mutation is in progress.
createErrorPaymentMethodError | nullError from the most recent create attempt.
updatePaymentMethod(vars: { paymentMethodId: string; updateData: any }) => voidUpdate an existing payment method's details.
updatePaymentMethodAsync(vars: { paymentMethodId: string; updateData: any }) => Promise<PaymentMethodData>Async version of updatePaymentMethod.
isUpdatingbooleantrue while an update mutation is in progress.
updateErrorPaymentMethodError | nullError from the most recent update attempt.
setDefaultPaymentMethod(paymentMethodId: string) => voidSet a payment method as the customer's default.
setDefaultPaymentMethodAsync(paymentMethodId: string) => Promise<{ success: boolean }>Async version of setDefaultPaymentMethod.
isSettingDefaultbooleantrue while the set-default mutation is in progress.
setDefaultErrorPaymentMethodError | nullError from the most recent set-default attempt.

Cache Management

PropertyTypeDescription
invalidateCache() => voidMark the payment methods cache as stale and trigger a refetch.
clearCache() => voidCompletely remove the payment methods cache.

Type Definitions

PaymentMethodData

interface PaymentMethodData {
id: string;
type: 'card';
card: {
brand: string;
last4: string;
funding: 'credit' | 'debit' | 'prepaid' | 'unknown';
country?: string;
fingerprint?: string;
};
billing_details?: {
name?: string;
email?: string;
phone?: string;
address?: {
line1?: string;
line2?: string;
city?: string;
state?: string;
postal_code?: string;
country?: string;
};
};
created: number;
metadata?: Record<string, string>;
is_default?: boolean;
}

PaymentMethodsResponse

interface PaymentMethodsResponse {
success: boolean;
data: PaymentMethodData[];
message?: string;
}

Implementation Details

  • Query Key: ['payment-methods'] (exported as PAYMENT_METHODS_QUERY_KEY)
  • Stale Time: 2 minutes
  • Garbage Collection Time: 10 minutes
  • Retry Logic: Up to 3 retries. Authentication errors (401, 403) are not retried.
  • Optimistic Updates: Delete, create, and update mutations apply optimistic cache updates before the server responds. On error, the cache is refetched to restore consistency.
  • Toast Notifications: Success and error toasts are displayed automatically via sonner.
  • API Endpoints:
    • GET /api/stripe/payment-methods/list -- Fetch all payment methods
    • DELETE /api/stripe/payment-methods/delete -- Delete a payment method
    • POST /api/stripe/payment-methods/create -- Create/attach a payment method
    • PUT /api/stripe/payment-methods/update -- Update a payment method
    • PATCH /api/stripe/payment-methods/update -- Set default payment method

Exported Utility Functions

getCardBrandInfo

Returns display metadata for a card brand.

function getCardBrandInfo(brand: string): {
color: string; // Tailwind gradient classes
text: string; // Short brand label (e.g., 'VISA', 'MC', 'AMEX')
bgColor: string; // Tailwind background class
}

Supported brands: visa, mastercard, amex / american_express, discover. Unknown brands fall back to gray styling.

isCardExpired

function isCardExpired(month: number, year: number): boolean

Returns true if the card's expiration date is in the past.

isCardExpiringSoon

function isCardExpiringSoon(month: number, year: number): boolean

Returns true if the card expires within the next 2 months but is not yet expired.

Usage Examples

Listing Payment Methods

import { usePaymentMethods, getCardBrandInfo } from '@/hooks/use-payment-methods';

function PaymentMethodsList() {
const { paymentMethods, isLoading, hasPaymentMethods } = usePaymentMethods();

if (isLoading) return <Spinner />;
if (!hasPaymentMethods) return <p>No payment methods on file.</p>;

return (
<ul>
{paymentMethods.map((method) => {
const brand = getCardBrandInfo(method.card.brand);
return (
<li key={method.id}>
<span className={brand.bgColor}>{brand.text}</span>
**** {method.card.last4}
{method.is_default && <Badge>Default</Badge>}
</li>
);
})}
</ul>
);
}

Deleting a Payment Method

function DeleteButton({ methodId }: { methodId: string }) {
const { deletePaymentMethod, isDeleting } = usePaymentMethods();

return (
<button onClick={() => deletePaymentMethod(methodId)} disabled={isDeleting}>
{isDeleting ? 'Deleting...' : 'Remove'}
</button>
);
}

Setting a Default Payment Method

function SetDefaultButton({ methodId }: { methodId: string }) {
const { setDefaultPaymentMethod, isSettingDefault } = usePaymentMethods();

return (
<button onClick={() => setDefaultPaymentMethod(methodId)} disabled={isSettingDefault}>
Set as Default
</button>
);
}