Unless I cancel my subscription... which I can't actually do yet - we'll add that
soon - in 1 month, Stripe will renew my subscription by automatically charging the
credit card I have on file. Eventually, we'll need to allow the user to update their
credit card info from right here on the account page. But let's start simple: by
at least reminding them which card they have on file by showing the card brand

like VISA - and the last 4 card numbers.

This is yet another piece of data that's already stored in Stripe, but we're
going to choose to also store it in our database, so we can quickly render the
info to the user.

But these are empty right now: we're not actually setting this data yet.

Before we do that, let's update the template to print these fields. Open the
profile/account.html.twig template. Down by the card details, let's say
if app.user.cardBrand, then print some information about the user's credit card,
like app.user.cardBrand ending in app.user.cardLast4:

53 lines app/Resources/views/profile/account.html.twig

... lines 1 - 2

{% block body %}

<divclass="nav-space">

<divclass="container">

... lines 6 - 11

<divclass="row">

<divclass="col-xs-6">

<tableclass="table">

<tbody>

... lines 16 - 31

<tr>

<th>Credit Card</th>

<td>

{% if app.user.cardBrand %}

{{ app.user.cardBrand }} ending in {{ app.user.cardLast4 }}

{% else %}

None

{% endif %}

</td>

</tr>

</tbody>

</table>

</div>

... lines 45 - 47

</div>

</div>

</div>

{% endblock %}

... lines 52 - 53

Those fields on the User object are empty now, so let's fix that!

The Card Details on the Stripe Customer

Head to the Stripe API docs and click on Customers. The card information is attached
to the customer under a field called sources. Yes, sources with an s at the end
because you could attach multiple cards to a customer if you wanted. But we're
not: on checkout, we set just one card on the customer, and replace any existing
card, if there was one.

In other words, sources will always have just one entry. That one entry will have
a data key, and that will describe the card: giving us all the info you see
here.

Now to the plan: use the Stripe API to populate the card information on the User
table right during checkout.

Setting the Card Details

In OrderController::chargeCustomer(), we either create or retrieve the
\Stripe\Customer. Assign both calls to a new $stripeCustomer variable:

127 lines src/AppBundle/Controller/OrderController.php

... lines 1 - 11

classOrderControllerextendsBaseController

{

... lines 14 - 83

privatefunctionchargeCustomer($token)

{

... lines 86 - 88

if (!$user->getStripeCustomerId()) {

$stripeCustomer = $stripeClient->createCustomer($user, $token);

} else {

$stripeCustomer = $stripeClient->updateCustomerCard($user, $token);

}

... lines 94 - 123

}

}

... lines 126 - 127

In StripeClient, the createCustomer() method already returns the \Stripe\Customer
object, so we're good here:

78 lines src/AppBundle/StripeClient.php

... lines 1 - 8

classStripeClient

{

... lines 11 - 19

publicfunctioncreateCustomer(User $user, $paymentToken)

{

$customer = \Stripe\Customer::create([

'email' => $user->getEmail(),

'source' => $paymentToken,

]);

... lines 26 - 29

return $customer;

}

... lines 33 - 76

}

The updateCustomerCard() method, however, retrieves the customer... but gets lazy
and doesn't return it. Fix that with return $customer:

78 lines src/AppBundle/StripeClient.php

... lines 1 - 8

classStripeClient

{

... lines 11 - 33

publicfunctionupdateCustomerCard(User $user, $paymentToken)

{

$customer = \Stripe\Customer::retrieve($user->getStripeCustomerId());

$customer->source = $paymentToken;

$customer->save();

return $customer;

}

... lines 43 - 76

}

Back in OrderController, we've got the \Stripe\Customer... so we're mega dangerous!
But instead of updating the fields on User right here, let's do it in SubscriptionHelper.
Add a new public function updateCardDetails() method with a User object that
should be updated and the \Stripe\Customer object that's associated with it:

Now, this is pretty easy: $cardDetails = $stripeCustomer->sources->data[0]. Then,
$user->setCardBrand($cardDetails) - go cheat with the Stripe API - the fields
we want are brand and last4. So, $cardDetails->brand. And
$user->setCardLast4($cardDetails->last4). Save only the user to the database with
the classic $this->em->persist($user) and $this->em->flush($user):