Understanding PaymentMethod Objects
A PaymentMethod is a Stripe object that securely stores payment method details. Unlike legacy approaches that handled tokens directly, PaymentMethod objects provide a structured way to work with different payment types through a consistent API. Each PaymentMethod contains type-specific information such as card details, bank account information, or wallet credentials, along with billing details that help ensure successful payment processing.
The PaymentMethod object serves as an intermediary between raw payment data and the actual charge. When you collect payment information, you create a PaymentMethod rather than directly accessing sensitive card numbers. This approach significantly reduces your PCI compliance burden since raw payment data never touches your servers. The PaymentMethod can then be attached to a Customer for future use or immediately used with a PaymentIntent to complete a transaction.
Stripe's architecture separates payment method storage from transaction execution. This separation offers several advantages: you can collect payment details before knowing the final payment amount, store multiple payment methods per customer, and reuse saved methods across different transactions. The decoupling also enables sophisticated features like setup intents that prepare payment methods for future charges without immediate billing. By understanding how PaymentMethod objects integrate with PaymentIntents, you can build flexible payment flows that adapt to various business requirements.
Key PaymentMethod Properties
Every PaymentMethod includes essential properties that define its identity and capabilities:
- ID: Unique identifier prefixed with
pm_followed by a random string - Type: Indicates the payment method type such as
card,us_bank_account, orlink - Billing Details: Name, email, phone, and address for verification and receipts
- Created Timestamp: When the PaymentMethod was created
- Customer Reference: The customer object the PaymentMethod is attached to, if any
The type property is particularly important because it determines which additional properties are available and how the PaymentMethod can be used. Understanding these properties helps you effectively manage and utilize PaymentMethods in your integration.
Creating PaymentMethods
Creating PaymentMethods typically involves client-side integration using Stripe's client libraries. On the web, Stripe.js provides secure elements that collect payment information without sensitive data passing through your servers. The payment element automatically handles different payment method types based on your configuration, presenting appropriate input fields for each type. When the customer submits the form, Stripe.js creates a PaymentMethod and returns it to your application.
Server-Side API Creation
The server-side API for creating PaymentMethods directly offers more control but requires careful handling of sensitive data. When creating a PaymentMethod directly via the API, you provide type-specific parameters such as card details or bank account information:
const paymentMethod = await stripe.paymentMethods.create({
type: 'card',
card: {
token: 'tok_visa',
},
billing_details: {
name: 'John Doe',
email: '[email protected]',
},
});
However, this approach requires maintaining PCI compliance since raw payment data passes through your systems. For most applications, using client-side creation with Stripe.js is the recommended approach. Our web development services team specializes in implementing secure payment integrations that minimize compliance overhead.
Stripe Elements and Payment Element
Stripe Elements provides pre-built UI components that simplify payment form creation. The Payment Element, in particular, automatically handles multiple payment method types in a single component. This means you can support cards, bank debits, and digital wallets without building separate form logic for each type. The Payment Element also automatically handles localization, showing appropriate payment methods based on the customer's location and your configuration.
Client-Side Implementation with Stripe.js
Implementing client-side PaymentMethod creation begins with loading Stripe.js and initializing the payment element. You create a container element in your HTML where the payment form will render, then initialize Stripe with your publishable key. The payment element connects to a PaymentIntent or SetupIntent that you create on your server, configuring which payment methods to accept based on your business needs and regional availability.
const stripe = Stripe('pk_test_publishable_key');
const elements = stripe.elements({
clientSecret: setupIntentClientSecret,
});
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
When the customer enters their payment information and submits the form, Stripe.js handles the sensitive data collection and creates a PaymentMethod. The PaymentMethod is then available for use with the associated Intent. This flow ensures that card numbers and other sensitive details never reach your servers, dramatically simplifying your PCI compliance requirements. Stripe.js also handles 3D Secure authentication when required, redirecting customers to verify their identity with their card issuer.
Supported Payment Method Types
Stripe supports an extensive range of payment method types, each with specific use cases and regional availability. Understanding these types helps you configure your integration to accept the payment methods most relevant to your customers.
Card Payments
Card payment methods encompass traditional credit and debit cards from major networks including Visa, Mastercard, American Express, and others. When a card PaymentMethod is created, it includes network, funding type, and last four digits information. You can also access card expiration dates and checksums, though you should avoid storing these details long-term. Cards support various authentication methods including 3D Secure, which provides additional security for transactions and helps shift liability for fraudulent charges.
Bank Debits
Bank debits represent an increasingly important payment method category. In the United States, ACH bank debits allow customers to pay directly from their bank accounts at lower costs than card transactions. Similar capabilities exist in Europe with SEPA debits and in the United Kingdom with Faster Payments. Bank debit payments typically require additional authorization steps and may have longer settlement times, but they offer significant cost savings for high-volume, lower-value transactions.
Digital Wallets
Digital wallets like Apple Pay, Google Pay, and Link provide convenient payment options that speed checkout by storing payment details securely. These wallets reduce friction by allowing customers to complete purchases with a single tap or click, without manually entering card details. Link, Stripe's own wallet solution, also remembers customer information for faster checkout across participating merchants.
For subscription-based businesses, understanding how these payment method types work with Stripe Billing is essential for maintaining reliable recurring revenue streams.
Attaching PaymentMethods to Customers
Attaching a PaymentMethod to a Customer object enables reuse across multiple transactions and simplifies payment management. When you attach a PaymentMethod, it becomes associated with that customer's account, allowing them to select it from their saved payment methods in future purchases. This attachment also enables features like automatic invoice payment for subscriptions and easier refund processing.
Default Payment Methods
You can set a PaymentMethod as the customer's default, which Stripe will automatically use for subscription invoices and other automatic charges. When a customer has a default payment method set, your recurring billing integrations can charge that method without requiring customer intervention each billing cycle:
await stripe.paymentMethods.attach(paymentMethodId, {
customer: customerId,
});
// Set as default
await stripe.customers.update(customerId, {
invoice_settings: {
default_payment_method: paymentMethodId,
},
});
Detaching and Lifecycle Management
PaymentMethods can be detached when no longer needed, either explicitly through the detach API or automatically when a customer is deleted. Some payment methods may become invalid over time, such as expired cards, and Stripe provides webhooks to notify you of these changes. Implement webhook handlers for payment_method.attached, payment_method.detached, and payment_method.updated to maintain accurate records.
Customer Self-Service
Customer payment method management often includes features for customers to view and manage their saved payment methods. The Stripe Customer Portal provides a pre-built interface for this purpose, allowing customers to add new PaymentMethods, remove old ones, and set defaults without requiring custom development. Integrating the Stripe Customer Portal into your application gives customers self-service capabilities while keeping your team focused on core product features.
PaymentIntents and PaymentMethods
PaymentMethods work in conjunction with PaymentIntents to process actual charges. A PaymentIntent represents a single payment flow and tracks the payment through its lifecycle from creation to completion. The PaymentMethod provides the payment instrument details, while the PaymentIntent manages the authorization, capture, and confirmation process. This separation allows you to create PaymentMethods in advance and use them across multiple PaymentIntents as needed.
Payment Flow
- Create a PaymentIntent on your server specifying amount, currency, and accepted payment methods
- Pass the PaymentIntent's client secret to client-side code
- Payment element renders appropriate form fields
- Customer submits payment details, creating a PaymentMethod and confirming the PaymentIntent
Authorization and Capture
When creating a PaymentIntent, you can set capture_method to manual for authorization-only flows, useful for orders that might change before final fulfillment:
const paymentIntent = await stripe.paymentIntents.create({
amount: 5000,
currency: 'usd',
capture_method: 'manual',
payment_method_types: ['card'],
});
You must capture the authorized amount within the authorization window, or the authorization will automatically expire, releasing the held funds.
Best Practices for Payment Method Management
Effective payment method management involves several key practices that improve customer experience and reduce operational complexity.
Security-First Approach
- Always use client-side payment element creation to minimize PCI compliance scope
- Never store raw card numbers or sensitive payment data
- Use PaymentMethod IDs for reference rather than payment details
- By collecting payment details through Stripe.js, you avoid handling raw card numbers and simplify your security audits
Comprehensive Error Handling
Payment methods can fail for numerous reasons: insufficient funds, expired cards, fraud blocks, or network issues. Your integration should gracefully handle these errors, providing clear guidance to customers on how to resolve issues. Implementing retry logic with appropriate delays helps recover from transient failures without requiring customer intervention.
Webhook Integration
- Listen for
payment_method.attached,payment_method.detached, andpayment_method.updatedevents - Keep payment method records synchronized with Stripe's state
- Handle expired or replaced payment methods proactively
Testing Thoroughly
Stripe provides extensive test card numbers that simulate various outcomes including successful charges, declines, and require actions. Create test cases for happy paths, error conditions, and edge cases like expired cards or cards requiring 3D Secure.
Security and PCI Compliance
Security considerations are paramount when handling payment information. While PaymentMethods reduce your PCI compliance scope significantly, understanding what remains your responsibility helps maintain a strong security posture.
PCI Compliance Levels
Using Stripe Elements or Payment Elements places you in the simplest compliance category (SAQ-A) because sensitive payment data never touches your servers. Your SAQ-A eligibility confirms that you handle only iframe-encapsulated payment data. If you create PaymentMethods server-side or store payment data, your compliance requirements increase accordingly.
Security Best Practices
- Use encrypted storage for any customer data
- Secure transmission using TLS for all API communications
- Implement access controls limiting who can view or modify payment information
- Maintain documentation of security practices for compliance assessments
Tokenization Benefits
Rather than storing raw card details, store the PaymentMethod ID and retrieve it from Stripe when needed. This approach means that even if your database is compromised, attackers cannot access usable payment credentials. Stripe's infrastructure handles the complexity of securely storing and updating payment method details, including handling card re-issues when customers receive replacement cards.
Implementation Example
Building a complete payment method implementation involves coordination between client and server components. The server creates the foundational objects--Customers and Intents--while the client handles secure payment information collection through Stripe's embedded elements.
Server-Side Implementation
// Create a customer
const customer = await stripe.customers.create({
email: '[email protected]',
name: 'John Doe',
});
// Create a SetupIntent for saving payment methods
const setupIntent = await stripe.setupIntents.create({
customer: customer.id,
payment_method_types: ['card', 'us_bank_account'],
});
// Return setupIntent.client_secret to client
Client-Side Implementation
// Initialize Stripe and Payment Element
const stripe = Stripe('pk_test_publishable_key');
const { error: submitError } = await stripe.confirmSetup({
elements,
confirmParams: {
return_url: 'https://yoursite.com/payment-complete',
},
});
Webhook Handling
app.post('/webhook', express.raw({ type: 'application/json' }),
async (req, res) => {
const event = req.body;
switch (event.type) {
case 'payment_method.attached':
// Update your database with the new payment method
break;
case 'payment_method.detached':
// Remove the payment method from your records
break;
case 'payment_method.updated':
// Handle updates like card expiration
break;
}
res.json({ received: true });
}
);
This flow provides a secure, user-friendly experience while maintaining strict separation between sensitive payment handling and your application logic.