/stripe-guides

How to update a payment method via Stripe API?

Learn how to update a payment method via the Stripe API with step-by-step instructions, code examples, error handling, and best practices for secure integration.

Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Book a free consultation

How to update a payment method via Stripe API?

How to Update a Payment Method via Stripe API: A Comprehensive Tutorial

 

Step 1: Set up your Stripe account and obtain API keys

 

Before you can update payment methods through the Stripe API, you need to have a Stripe account and API keys. Here's how to get them:

  • Sign up for a Stripe account at https://stripe.com/
  • Navigate to the Developers section in your Stripe Dashboard
  • Find your API keys (both publishable and secret keys)
  • For testing purposes, use the test API keys provided by Stripe

 

Step 2: Install the Stripe library for your programming language

 

Stripe offers libraries for various programming languages. Here's how to install them for the most common languages:

For Node.js:

npm install stripe

For Python:

pip install stripe

For PHP:

composer require stripe/stripe-php

For Ruby:

gem install stripe

 

Step 3: Initialize the Stripe client with your API key

 

Now, let's initialize the Stripe client with your secret API key:

For Node.js:

const stripe = require('stripe')('sk_test_your_secret_key');

// For async/await support
async function updatePaymentMethod() {
  // We'll add code here in the next steps
}

For Python:

import stripe
stripe.api_key = 'sk_test_your_secret\_key'

For PHP:

For Ruby:

require 'stripe'
Stripe.api_key = 'sk_test_your_secret\_key'

 

Step 4: Retrieve the payment method to update

 

Before updating a payment method, you need to retrieve it using its ID:

For Node.js:

async function updatePaymentMethod() {
  try {
    // Retrieve the payment method
    const paymentMethod = await stripe.paymentMethods.retrieve(
      'pm\_123456789'
    );
    console.log('Retrieved payment method:', paymentMethod);
    
    // We'll add update code in the next step
  } catch (error) {
    console.error('Error retrieving payment method:', error);
  }
}

For Python:

try:
    # Retrieve the payment method
    payment_method = stripe.PaymentMethod.retrieve('pm_123456789')
    print(f"Retrieved payment method: {payment\_method}")
    
    # We'll add update code in the next step
except stripe.error.StripeError as e:
    print(f"Error retrieving payment method: {e}")

For PHP:

getMessage();
}

For Ruby:

begin
  # Retrieve the payment method
  payment_method = Stripe::PaymentMethod.retrieve('pm_123456789')
  puts "Retrieved payment method: #{payment\_method}"
  
  # We'll add update code in the next step
rescue Stripe::StripeError => e
  puts "Error retrieving payment method: #{e.message}"
end

 

Step 5: Update the payment method

 

Now, let's update the payment method. You can update billing details such as the cardholder's name, address, email, phone, etc. Note that you cannot update the actual card number, expiration date, or CVC after a payment method has been created.

For Node.js:

async function updatePaymentMethod() {
  try {
    // Retrieve the payment method first
    const paymentMethod = await stripe.paymentMethods.retrieve('pm\_123456789');
    console.log('Retrieved payment method:', paymentMethod);
    
    // Update the payment method
    const updatedPaymentMethod = await stripe.paymentMethods.update(
      'pm\_123456789',
      {
        billing\_details: {
          name: 'Jane Doe',
          email: '[email protected]',
          phone: '+14155552671',
          address: {
            line1: '123 Main St',
            city: 'San Francisco',
            state: 'CA',
            postal\_code: '94111',
            country: 'US',
          },
        },
      }
    );
    
    console.log('Updated payment method:', updatedPaymentMethod);
    return updatedPaymentMethod;
  } catch (error) {
    console.error('Error updating payment method:', error);
    throw error;
  }
}

// Call the function
updatePaymentMethod();

For Python:

try:
    # Retrieve the payment method first
    payment_method = stripe.PaymentMethod.retrieve('pm_123456789')
    print(f"Retrieved payment method: {payment\_method}")
    
    # Update the payment method
    updated_payment_method = stripe.PaymentMethod.modify(
        'pm\_123456789',
        billing\_details={
            'name': 'Jane Doe',
            'email': '[email protected]',
            'phone': '+14155552671',
            'address': {
                'line1': '123 Main St',
                'city': 'San Francisco',
                'state': 'CA',
                'postal\_code': '94111',
                'country': 'US',
            },
        }
    )
    
    print(f"Updated payment method: {updated_payment_method}")
except stripe.error.StripeError as e:
    print(f"Error updating payment method: {e}")

For PHP:

 [
                'name' => 'Jane Doe',
                'email' => '[email protected]',
                'phone' => '+14155552671',
                'address' => [
                    'line1' => '123 Main St',
                    'city' => 'San Francisco',
                    'state' => 'CA',
                    'postal\_code' => '94111',
                    'country' => 'US',
                ],
            ],
        ]
    );
    
    echo "Updated payment method: " . $updated_payment_method;
} catch (\Stripe\Exception\ApiErrorException $e) {
    echo "Error updating payment method: " . $e->getMessage();
}

For Ruby:

begin
  # Retrieve the payment method first
  payment_method = Stripe::PaymentMethod.retrieve('pm_123456789')
  puts "Retrieved payment method: #{payment\_method}"
  
  # Update the payment method
  updated_payment_method = Stripe::PaymentMethod.update(
    'pm\_123456789',
    {
      billing\_details: {
        name: 'Jane Doe',
        email: '[email protected]',
        phone: '+14155552671',
        address: {
          line1: '123 Main St',
          city: 'San Francisco',
          state: 'CA',
          postal\_code: '94111',
          country: 'US',
        },
      },
    }
  )
  
  puts "Updated payment method: #{updated_payment_method}"
rescue Stripe::StripeError => e
  puts "Error updating payment method: #{e.message}"
end

 

Step 6: Implement error handling

 

Proper error handling is crucial when working with APIs. Let's enhance our error handling:

For Node.js:

async function updatePaymentMethod(paymentMethodId, billingDetails) {
  try {
    // Update the payment method
    const updatedPaymentMethod = await stripe.paymentMethods.update(
      paymentMethodId,
      { billing\_details: billingDetails }
    );
    
    return {
      success: true,
      paymentMethod: updatedPaymentMethod
    };
  } catch (error) {
    console.error('Error updating payment method:', error);
    
    // Handle specific error types
    if (error.type === 'StripeInvalidRequestError') {
      return {
        success: false,
        error: 'Invalid payment method ID or parameters',
        details: error.message
      };
    } else if (error.type === 'StripeAuthenticationError') {
      return {
        success: false,
        error: 'Authentication with Stripe failed',
        details: error.message
      };
    } else {
      return {
        success: false,
        error: 'An unexpected error occurred',
        details: error.message
      };
    }
  }
}

// Example usage
const billingDetails = {
  name: 'Jane Doe',
  email: '[email protected]',
  phone: '+14155552671',
  address: {
    line1: '123 Main St',
    city: 'San Francisco',
    state: 'CA',
    postal\_code: '94111',
    country: 'US',
  },
};

updatePaymentMethod('pm\_123456789', billingDetails)
  .then(result => {
    if (result.success) {
      console.log('Payment method updated successfully:', result.paymentMethod);
    } else {
      console.error('Failed to update payment method:', result.error, result.details);
    }
  });

 

Step 7: Attach the updated payment method to a customer (optional)

 

If you need to attach the updated payment method to a customer, you can do so with the following code:

For Node.js:

async function attachPaymentMethodToCustomer(paymentMethodId, customerId) {
  try {
    const paymentMethod = await stripe.paymentMethods.attach(
      paymentMethodId,
      { customer: customerId }
    );
    
    return {
      success: true,
      paymentMethod
    };
  } catch (error) {
    console.error('Error attaching payment method to customer:', error);
    return {
      success: false,
      error: error.message
    };
  }
}

// Example usage
attachPaymentMethodToCustomer('pm_123456789', 'cus_123456789')
  .then(result => {
    if (result.success) {
      console.log('Payment method attached to customer:', result.paymentMethod);
    } else {
      console.error('Failed to attach payment method:', result.error);
    }
  });

For Python:

def attach_payment_method_to_customer(payment_method_id, customer\_id):
    try:
        payment\_method = stripe.PaymentMethod.attach(
            payment_method_id,
            customer=customer\_id
        )
        
        return {
            'success': True,
            'payment_method': payment_method
        }
    except stripe.error.StripeError as e:
        print(f"Error attaching payment method to customer: {e}")
        return {
            'success': False,
            'error': str(e)
        }

# Example usage
result = attach_payment_method_to_customer('pm_123456789', 'cus_123456789')
if result['success']:
    print(f"Payment method attached to customer: {result['payment\_method']}")
else:
    print(f"Failed to attach payment method: {result['error']}")

 

Step 8: Set the updated payment method as the default (optional)

 

If you want to set the updated payment method as the default for future payments, you can update the customer's default payment method:

For Node.js:

async function setDefaultPaymentMethod(customerId, paymentMethodId) {
  try {
    const customer = await stripe.customers.update(
      customerId,
      { invoice_settings: { default_payment\_method: paymentMethodId } }
    );
    
    return {
      success: true,
      customer
    };
  } catch (error) {
    console.error('Error setting default payment method:', error);
    return {
      success: false,
      error: error.message
    };
  }
}

// Example usage
setDefaultPaymentMethod('cus_123456789', 'pm_123456789')
  .then(result => {
    if (result.success) {
      console.log('Default payment method updated:', result.customer);
    } else {
      console.error('Failed to update default payment method:', result.error);
    }
  });

For Python:

def set_default_payment_method(customer_id, payment_method_id):
    try:
        customer = stripe.Customer.modify(
            customer\_id,
            invoice_settings={'default_payment_method': payment_method\_id}
        )
        
        return {
            'success': True,
            'customer': customer
        }
    except stripe.error.StripeError as e:
        print(f"Error setting default payment method: {e}")
        return {
            'success': False,
            'error': str(e)
        }

# Example usage
result = set_default_payment_method('cus_123456789', 'pm\_123456789')
if result['success']:
    print(f"Default payment method updated: {result['customer']}")
else:
    print(f"Failed to update default payment method: {result['error']}")

 

Step 9: Create a complete implementation example

 

Let's put everything together in a complete example that retrieves, updates, and manages a payment method:

For Node.js:

const stripe = require('stripe')('sk_test_your_secret_key');

async function managePaymentMethod(paymentMethodId, customerId, billingDetails) {
  try {
    // Step 1: Retrieve the payment method
    const paymentMethod = await stripe.paymentMethods.retrieve(paymentMethodId);
    console.log('Retrieved payment method:', paymentMethod.id);
    
    // Step 2: Update the payment method
    const updatedPaymentMethod = await stripe.paymentMethods.update(
      paymentMethodId,
      { billing\_details: billingDetails }
    );
    console.log('Updated payment method:', updatedPaymentMethod.id);
    
    // Step 3: Attach payment method to customer if not already attached
    if (!updatedPaymentMethod.customer) {
      const attachedPaymentMethod = await stripe.paymentMethods.attach(
        paymentMethodId,
        { customer: customerId }
      );
      console.log('Payment method attached to customer:', attachedPaymentMethod.id);
    }
    
    // Step 4: Set as default payment method
    const customer = await stripe.customers.update(
      customerId,
      { invoice_settings: { default_payment\_method: paymentMethodId } }
    );
    console.log('Default payment method updated for customer:', customer.id);
    
    return {
      success: true,
      paymentMethod: updatedPaymentMethod,
      customer
    };
  } catch (error) {
    console.error('Error managing payment method:', error);
    return {
      success: false,
      error: error.message
    };
  }
}

// Example usage
const billingDetails = {
  name: 'Jane Doe',
  email: '[email protected]',
  phone: '+14155552671',
  address: {
    line1: '123 Main St',
    city: 'San Francisco',
    state: 'CA',
    postal\_code: '94111',
    country: 'US',
  },
};

// Call the function
managePaymentMethod('pm_123456789', 'cus_123456789', billingDetails)
  .then(result => {
    if (result.success) {
      console.log('Payment method management completed successfully');
    } else {
      console.error('Payment method management failed:', result.error);
    }
  });

 

Step 10: Implement a front-end integration (optional)

 

If you want to update payment methods from a front-end application, you'll need to create a secure integration. Here's how to do it with React:

// React component example
import React, { useState } from 'react';
import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';
import axios from 'axios';

const UpdatePaymentMethodForm = ({ paymentMethodId, customerId }) => {
  const stripe = useStripe();
  const elements = useElements();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [success, setSuccess] = useState(false);
  const [billingDetails, setBillingDetails] = useState({
    name: '',
    email: '',
    phone: '',
    address: {
      line1: '',
      city: '',
      state: '',
      postal\_code: '',
      country: '',
    },
  });

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    
    if (name.includes('.')) {
      const [parent, child] = name.split('.');
      setBillingDetails({
        ...billingDetails,
        [parent]: {
          ...billingDetails[parent],
          [child]: value,
        },
      });
    } else {
      setBillingDetails({
        ...billingDetails,
        [name]: value,
      });
    }
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError(null);
    
    try {
      // Call your backend API to update the payment method
      const response = await axios.post('/api/update-payment-method', {
        paymentMethodId,
        customerId,
        billingDetails,
      });
      
      if (response.data.success) {
        setSuccess(true);
      } else {
        setError(response.data.error);
      }
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    

Update Payment Method

{error &&
{error}
} {success &&
Payment method updated successfully!
}
); }; export default UpdatePaymentMethodForm;

And here's the corresponding backend endpoint to handle the update request:

// Express.js backend endpoint example
const express = require('express');
const router = express.Router();
const stripe = require('stripe')('sk_test_your_secret_key');

router.post('/api/update-payment-method', async (req, res) => {
  const { paymentMethodId, customerId, billingDetails } = req.body;
  
  try {
    // Update the payment method
    const updatedPaymentMethod = await stripe.paymentMethods.update(
      paymentMethodId,
      { billing\_details: billingDetails }
    );
    
    // Set as default payment method if customer ID is provided
    if (customerId) {
      await stripe.customers.update(
        customerId,
        { invoice_settings: { default_payment\_method: paymentMethodId } }
      );
    }
    
    res.json({
      success: true,
      paymentMethod: updatedPaymentMethod
    });
  } catch (error) {
    console.error('Error updating payment method:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

module.exports = router;

 

Step 11: Testing your implementation

 

To properly test your payment method update functionality, follow these steps:

  • Use Stripe's test API keys (starting with 'sk_test_')
  • Use Stripe's test card numbers (e.g., 4242 4242 4242 4242)
  • Create a test payment method using the Stripe API or dashboard
  • Try updating various billing details
  • Verify the updates in your Stripe dashboard
  • Test error scenarios (invalid payment method ID, etc.)

To help with testing, here's a script that creates a test payment method you can use:

const stripe = require('stripe')('sk_test_your_secret_key');

async function createTestPaymentMethod() {
  try {
    const paymentMethod = await stripe.paymentMethods.create({
      type: 'card',
      card: {
        number: '4242424242424242',
        exp\_month: 12,
        exp\_year: 2025,
        cvc: '123',
      },
      billing\_details: {
        name: 'Initial Name',
        email: '[email protected]',
      },
    });
    
    console.log('Created test payment method:', paymentMethod.id);
    return paymentMethod;
  } catch (error) {
    console.error('Error creating test payment method:', error);
    throw error;
  }
}

createTestPaymentMethod();

 

Conclusion

 

Updating payment methods via the Stripe API involves a few key steps: retrieving the existing payment method, updating its billing details, and optionally attaching it to a customer or setting it as the default payment method. Remember that you cannot update the actual card details (number, expiration date, CVC) after a payment method has been created. To change those, you'll need to create a new payment method.

Keep in mind that proper error handling and security practices are essential when working with payment information. Always use HTTPS, validate input, and follow PCI compliance guidelines when handling payment data.

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Book a Free Consultation

Client trust and success are our top priorities

When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.

Rapid Dev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with. They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

CPO, Praction - Arkady Sokolov

May 2, 2023

Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost. He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Co-Founder, Arc - Donald Muir

Dec 27, 2022

Rapid Dev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space. They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Co-CEO, Grantify - Mat Westergreen-Thorne

Oct 15, 2022

Rapid Dev is an excellent developer for no-code and low-code solutions.
We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Co-Founder, Church Real Estate Marketplace - Emmanuel Brown

May 1, 2024 

Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 
This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Production Manager, Media Production Company - Samantha Fekete

Sep 23, 2022