If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
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.
"Houston: no signs of life"
Start the conversation!
// 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
}
}