If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Back on the account page, our customers would love us if we would show them when they will be billed next. In other words: when will my subscription renew?
Open the Subscription
class - aka the subscription table. I've already added a
billingPeriodEndsAt
column:
... lines 1 - 10 | |
class Subscription | |
{ | |
... lines 13 - 40 | |
/** | |
* @ORM\Column(type="datetime", nullable=true) | |
*/ | |
private $billingPeriodEndsAt; | |
... lines 45 - 86 | |
/** | |
* @return \DateTime | |
*/ | |
public function getBillingPeriodEndsAt() | |
{ | |
return $this->billingPeriodEndsAt; | |
} | |
... lines 94 - 100 | |
} |
All we need to do is set this when the subscription is first created. Ok, we also need to update it each month when the subscription is renewed - but we'll talk about that later with webhooks.
The Subscription
is created - or retrieved - right here in SubscriptionHelper
.
This is the spot to set that date.
And hey! This method is passed a \Stripe\Subscription
object:
... lines 1 - 8 | |
class SubscriptionHelper | |
{ | |
... lines 11 - 45 | |
public function addSubscriptionToUser(\Stripe\Subscription $stripeSubscription, User $user) | |
{ | |
... lines 48 - 60 | |
} | |
... lines 62 - 70 | |
} |
Let's check out the API docs to see what that gives us. Oh yes, it has
a current_period_end
property, which is a UNIX timestamp. Bingo!
In SubscriptionHelper
, before we activate the subscription, add a new $periodEnd
variable. To convert the timestamp to a \DateTime
object, say
$periodEnd = \DateTime::createFromFormat('U')
- for UNIX timestamp -
$stripeSubscription->current_period_end
:
... lines 1 - 8 | |
class SubscriptionHelper | |
{ | |
... lines 11 - 45 | |
public function addSubscriptionToUser(\Stripe\Subscription $stripeSubscription, User $user) | |
{ | |
$subscription = $user->getSubscription(); | |
if (!$subscription) { | |
$subscription = new Subscription(); | |
$subscription->setUser($user); | |
} | |
$periodEnd = \DateTime::createFromFormat('U', $stripeSubscription->current_period_end); | |
... lines 55 - 62 | |
} | |
... lines 64 - 72 | |
} |
Now, pass that into the activateSubscription()
method as a new argument:
... lines 1 - 53 | |
$periodEnd = \DateTime::createFromFormat('U', $stripeSubscription->current_period_end); | |
$subscription->activateSubscription( | |
$stripeSubscription->plan->id, | |
$stripeSubscription->id, | |
$periodEnd | |
); | |
... lines 60 - 74 |
Open that function in Subscription
and add a new \DateTime
argument called $periodEnd
.
Set the property with $this->billingPeriodEndsAt = $periodEnd
:
... lines 1 - 10 | |
class Subscription | |
{ | |
... lines 13 - 94 | |
public function activateSubscription($stripePlanId, $stripeSubscriptionId, \DateTime $periodEnd) | |
{ | |
... lines 97 - 98 | |
$this->billingPeriodEndsAt = $periodEnd; | |
... line 100 | |
} | |
} |
Done!
To celebrate, open the account.html.twig
template. For "Next Billing At", add
if app.user.subscription
- so if they have a subscription - then print
app.user.subscription.billingPeriodEndsAt|date('F jS')
to format the date
nicely:
... lines 1 - 2 | |
{% block body %} | |
<div class="nav-space"> | |
<div class="container"> | |
... lines 6 - 11 | |
<div class="row"> | |
<div class="col-xs-6"> | |
<table class="table"> | |
<tbody> | |
... lines 16 - 25 | |
<tr> | |
<th>Next Billing at:</th> | |
<td> | |
{% if app.user.subscription %} | |
{{ app.user.subscription.billingPeriodEndsAt|date('F jS') }} | |
{% else %} | |
n/a | |
{% endif %} | |
</td> | |
</tr> | |
... lines 36 - 45 | |
</tbody> | |
</table> | |
</div> | |
... lines 49 - 51 | |
</div> | |
</div> | |
</div> | |
{% endblock %} | |
... lines 56 - 57 |
OK team! Refresh that page! The "Next Billing At" is... wrong! August 9th! That's today! But no worries, that's just because the field is blank in the database, so it's using today. To really test if this is working, we need to checkout with a new subscription.
Now, in real life, you probably won't allow your users to buy multiple subscriptions. Afterall, we're only storing info in the database about one Subscription, per user. But, for testing, it's really handy to be able to checkout over and over again.
The checkout worked! Click "Account". Yes! There is the correct date: September 9th, one month from today. And the VISA card ends in 4242.
Alright: the informational part of the account page is done. But, the user still needs to be able to do some pretty important stuff, like cancelling their subscription - yes, this does happen, it's nothing personal - and updating their credit card. Let's get to it.
"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
}
}