Handling Customer Card Updates on Your Website

Use a simple Checkout form and a PHP script to collect and update card details for your customers. If you need help after reading this, check out our answers to common questions or chat live with other developers in #stripe on freenode.

Sometimes your customers need to change the card info that they have on record with your business, whether that’s just to swap in a preferred card, or because the bank issued a replacement. Though Stripe does take care of updating expiring cards for you, the ability to change the card a customer has on file is something you’ll likely want to enable for your users.

The good news is that updating the customer’s payment source in Stripe is pretty straightforward. The process is almost the exact same as that used to create the customer and store their card details initially, with just a couple of minor changes.

Thinking through the implementation

The focus in this recipe is on how to use Stripe to update a customer’s card, so it will help to first think through what the entire process might look like for your application.

  1. On your website, your customer clicks a link to access their profile or payment details page where they can update their payment info.
  2. Through a form on your site, they add their new card details. The payment details are securely sent directly to Stripe.
  3. Stripe returns a token representing the new card details. This token is then submitted to your server to be used in an API call.
  4. Your script obtains, from storage on your site, the Stripe ID of the customer that needs to be updated. This would likely be stored in a database.
  5. The token is used in your script to update the customer’s default payment source in Stripe.
  6. Your application displays a message to the customer to let them know the update was successful.

One important thing to note here is you’ll need the Stripe customer ID to make the update request. Presumably you have some sort of user table that tracks your users and allows them to log in. This table could also store the user’s Stripe customer ID, created when they first registered at your site.

user_id email pass stripe_customer created_at
1 user@email.com KJAoe8c$jdy2@idsaje cus_aBCdef123456 2015-11-26

Creating an “update your card” form

The first step is to tokenize the details of the customer’s new card. This process should look familiar, since you’ve likely used the same functionality to obtain their initial card details. The only real difference here is that you’re updating the Customer object in Stripe to use this new card instead of creating a Customer object with it.

Since Checkout handles styling and input validation out-of-the-box, it’s the solution used in this example. If having full control over the look and feel of the form is more your style, you could use Stripe.js and a custom form to handle this instead. To get started, create a new PHP file called update.php and add the Checkout form below:

<form action="" method="POST">
  <script
  src="https://checkout.stripe.com/checkout.js" class="stripe-button"
  data-key="pk_test_6pRNASCoBOKtIshFeQd4XMUh"
  data-image="/path/to/your/logo.png"
  data-name="Your Website Name"
  data-panel-label="Update Card Details"
  data-label="Update Card Details"
  data-allow-remember-me=false
  data-locale="auto">
  </script>
</form>

Notice that the data-amount attribute isn’t set, which prevents an amount from being presented in Checkout. Instead, the text is “Update Card Details”. As written, the customer also has to enter their email address again here, which is a bit suboptimal, so a better solution is explained at the end of this recipe.

The Dashboard includes a colored dot indicating the risk level for charges that are above-normal risk

As a reminder, this particular version of Checkout would only be displayed on your site to logged-in users wanting to update their payment details.

Using the token to update the customer’s card

Next, you need some server-side code to update the customer’s card in Stripe, so create a script called update_card.php. This script will handle receiving the token that’s submitted to your server, and will use that token to update the Customer object.

This article assumes that you’re already using Stripe within your app to create customers and charges, so you’ll want to be sure you load the Stripe library and authenticate using your API key as a first step in your update_card.php file.

After authenticating, add the following code to your update_card.php script to obtain the token that was submitted and update the Customer object:

<?php
// Updates the payment source on file for a customer

// If you're using Composer, use Composer's autoload
require_once('vendor/autoload.php');

// Use your test API key (switch to the live key later)
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

if (isset($_POST['stripeToken'])){
  try {
    $cu = \Stripe\Customer::retrieve($customer_id); // stored in your application
    $cu->source = $_POST['stripeToken']; // obtained with Checkout
    $cu->save();

    $success = "Your card details have been updated!";
  }
  catch(\Stripe\Error\Card $e) {

    // Use the variable $error to save any errors
    // To be displayed to the customer later in the page
    $body = $e->getJsonBody();
    $err  = $body['error'];
    $error = $err['message'];
  }
  // Add additional error handling here as needed
}
?>

We’ve placed a random API key in the code. Replace this with your actual API key to test this code for yourself.

Putting it all together

Require the update_card.php file immediately at the top of your update.php page before your Checkout form. As a simple trick, you can have it be included only upon form submission:

<?php if ($_SERVER['REQUEST_METHOD'] == 'POST') require_once('./update_card.php'); ?>
...

Your customer will want to know when their card details have been updated, so the final step is to add a message letting them know the update was successful. Before the form tag in your update.php page, output the error or success message:

<?php
if (isset($error)) {
  echo $error;
} elseif (isset($success)) {
  echo $success;
}
?>

This example uses some pretty blunt error handling, so you’ll likely want to read through our error handling documentation and code something a bit more professional, along with applying error and success CSS styles to match your application.

Extending this recipe

This recipe outlines a simple implementation you can use to update cards for your customers through your website. Two ways you may want to expand these concepts to provide an even better experience for your user include setting the email address for them and displaying information about the current card on file.

Adding the data-email attribute

To save your customers from having to enter their email address in the Checkout form, provide the customer’s current email address (obtained from your app just like the customer ID) as the data-email attribute value in Checkout. Doing so pre-fills the email address in the displayed Checkout form. Assuming the variable $email contains the email address of the user stored within and retrieved from your application, your code might look like this:

<form action="" method="POST">
  <script
  src="https://checkout.stripe.com/checkout.js" class="stripe-button"
  data-key="pk_test_6pRNASCoBOKtIshFeQd4XMUh"
  data-image="/path/to/your/logo.png"
  data-name="Your Website Name"
  data-panel-label="Update Card Details"
  data-label="Update Card Details"
  data-allow-remember-me=false
  data-locale="auto"
  data-email="<?php echo $email; ?>">
  </script>
</form>

Find other Checkout configuration options in the docs.

Displaying the default card on record

Just before the card update form, you could display the current default card for the customer by using the retrieve a customer API call to get that information from Stripe. The following code expands the customer’s default_source object to fetch the card details with a single API call.

<?php
// Fetch the details of a customer's default payment source

// If you're using Composer, use Composer's autoload
require_once('vendor/autoload.php');

// Use your test API key (switch to the live key later)
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

// Retrieve the customer and expand their default source
$cu = \Stripe\Customer::Retrieve(
  array("id" => $customer_id, "expand" => array("default_source"))
);

// Echo the card brand and last 4 digits
echo $cu->default_source->brand . " ending in " . $cu->default_source->last4;