Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This course is archived!
This tutorial uses an older version of Symfony of the stripe-php SDK. The majority of the concepts are still valid, though there *are* differences. We've done our best to add notes & comments that describe these changes.

Handling Card Update Fails

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

There are actually two ways for the credit card update to fail. Most failures happen immediately, and are handle via JavaScript. But a few don't happen until we try to attach the card to the customer.

Let's see an example. In Stripe's documentation, find the "Testing" section - it's under the Payments header in the new design. Down the page a bit, you'll find a table full of cards that will work or fail for different reasons. Find the one that ends in 0002 and copy it. Fill the form out using this and update the card.

Ah, 500 error!

Your card was declined

Ok, a \Stripe\Error\Card exception was thrown the moment that we tried to save the new customer card back to Stripe. We did handle this situation on our checkout page, so we just need to also handle it here.

In ProfileController, the updateCustomerCard() call is the one that might fail. Wrap this is a try-catch for \Stripe\Error\Card:

... lines 1 - 12
class ProfileController extends BaseController
{
... lines 15 - 72
public function updateCreditCardAction(Request $request)
{
... lines 75 - 77
try {
$stripeClient = $this->get('stripe_client');
$stripeCustomer = $stripeClient->updateCustomerCard(
$user,
$token
);
} catch (\Stripe\Error\Card $e) {
... lines 85 - 89
}
... lines 91 - 99
}

Set an $error variable to: There was a problem charging your card and then concatenate $e->getMessage():

... lines 1 - 12
class ProfileController extends BaseController
{
... lines 15 - 72
public function updateCreditCardAction(Request $request)
{
... lines 75 - 77
try {
$stripeClient = $this->get('stripe_client');
$stripeCustomer = $stripeClient->updateCustomerCard(
$user,
$token
);
} catch (\Stripe\Error\Card $e) {
$error = 'There was a problem charging your card: '.$e->getMessage();
... lines 86 - 89
}
... lines 91 - 99
}

To show this to the user, call addFlash() and set an error type:

... lines 1 - 12
class ProfileController extends BaseController
{
... lines 15 - 72
public function updateCreditCardAction(Request $request)
{
... lines 75 - 77
try {
$stripeClient = $this->get('stripe_client');
$stripeCustomer = $stripeClient->updateCustomerCard(
$user,
$token
);
} catch (\Stripe\Error\Card $e) {
$error = 'There was a problem charging your card: '.$e->getMessage();
$this->addFlash('error', $error);
... lines 88 - 89
}
... lines 91 - 99
}

Just like with success flash messages, our base template is already configured to show these. But in this case, the message will look red and angry!

Finally, redirect back to the profile_account route:

... lines 1 - 12
class ProfileController extends BaseController
{
... lines 15 - 72
public function updateCreditCardAction(Request $request)
{
... lines 75 - 77
try {
$stripeClient = $this->get('stripe_client');
$stripeCustomer = $stripeClient->updateCustomerCard(
$user,
$token
);
} catch (\Stripe\Error\Card $e) {
$error = 'There was a problem charging your card: '.$e->getMessage();
$this->addFlash('error', $error);
return $this->redirectToRoute('profile_account');
}
... lines 91 - 99
}

To try it out, go back, press "Update Card" again, and use the same, failing card number. This time, no 500 error! Just this sad, but useful message.

With the ability to subscribe, cancel and update their credit card info, our subscription system is up-and-running! Now it's time to face our last big, required topic: webhooks. How can we email a user if we're having problems charging their card? How would we know when Stripe cancel's a customer's subscription due to payment failure? And how can we email a receipt each month when a subscription renews?

The answer to all of these is: webhooks. And getting those right will make your system really rock.

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses an older version of Symfony of the stripe-php SDK. The majority of the concepts are still valid, though there *are* differences. We've done our best to add notes & comments that describe these changes.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9, <7.4",
        "symfony/symfony": "3.1.*", // v3.1.10
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.8
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.2
        "symfony/monolog-bundle": "^2.8", // v2.12.1
        "symfony/polyfill-apcu": "^1.0", // v1.3.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "~2.0.1", // v2.0.1
        "stof/doctrine-extensions-bundle": "^1.2", // v1.2.2
        "stripe/stripe-php": "^3.15", // v3.23.0
        "doctrine/doctrine-migrations-bundle": "^1.1", // v1.2.1
        "phpunit/phpunit": "^5.5", // 5.7.20
        "composer/package-versions-deprecated": "^1.11" // 1.11.99
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.4
        "symfony/phpunit-bridge": "^3.0", // v3.3.0
        "hautelook/alice-bundle": "^1.3", // v1.4.1
        "doctrine/data-fixtures": "^1.2" // v1.2.2
    }
}
userVoice