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.
Add the Subscription to your Cart
We can already add products to our cart... but a user should also be able to click these fancy buttons and add a subscription to their cart.
Open up OrderController
: the home for the checkout and shopping cart magic. I've
already started a new page called addSubscriptionToCartAction()
:
// ... lines 1 - 11 | |
class OrderController extends BaseController | |
{ | |
// ... lines 14 - 27 | |
/** | |
* @Route("/cart/subscription/{planId}", name="order_add_subscription_to_cart") | |
*/ | |
public function addSubscriptionToCartAction($planId) | |
{ | |
// todo - add the subscription plan to the cart! | |
} | |
// ... lines 35 - 96 | |
} | |
// ... lines 98 - 99 |
When we're done, if the user goes to /cart/subscription/farmer_brent_monthly
,
this should put that into the cart.
First, hook up the buttons to point here. The template for this page lives at
app/Resources/views/product/pricing.html.twig
:
// ... lines 1 - 3 | |
{% block body %} | |
<div class="container"> | |
// ... lines 7 - 10 | |
<div class="row"> | |
<div class="col-md-6"> | |
<div class="price-square"> | |
<p class="pricing-length-header monthly text-center">Farmer Brent</p> | |
<div class="pricing-info-padding"> | |
<p class="price text-center">$99</p> | |
<p class="text-center">A fresh set of shears monthly along with our beloved after-shear in one of two fragrances!</p> | |
</div> | |
<a href="{{ path('order_add_subscription_to_cart', { | |
'planId': 'TODO' | |
}) }}" class="btn btn-warning center-block price-btn"> | |
Shear Me! | |
</a> | |
</div> | |
</div> | |
<div class="col-md-6"> | |
<div class="price-square price-square-yearly"> | |
<p class="pricing-length-header yearly text-center">New Zealander</p> | |
<div class="pricing-info-padding"> | |
<p class="price text-center">$199</p> | |
<p class="text-center">The perks of a Farmer Brent membership, but with a fresh bottle of conditioner and a comb each month!</p> | |
</div> | |
<a href="{{ path('order_add_subscription_to_cart', { | |
'planId': 'TODO' | |
}) }}" class="btn btn-warning center-block price-btn"> | |
Get Shearing! | |
</a> | |
</div> | |
</div> | |
</div> | |
// ... lines 43 - 102 | |
</div> | |
{% endblock %} |
I started adding the link to this page, but left the plan ID blank. Fill 'em in!
farmer_brent_monthly
and then down below, new_zealander_monthly
:
// ... lines 1 - 3 | |
{% block body %} | |
<div class="container"> | |
// ... lines 7 - 10 | |
<div class="row"> | |
<div class="col-md-6"> | |
<div class="price-square"> | |
<p class="pricing-length-header monthly text-center">Farmer Brent</p> | |
<div class="pricing-info-padding"> | |
<p class="price text-center">$99</p> | |
<p class="text-center">A fresh set of shears monthly along with our beloved after-shear in one of two fragrances!</p> | |
</div> | |
<a href="{{ path('order_add_subscription_to_cart', { | |
'planId': 'farmer_brent_monthly' | |
}) }}" class="btn btn-warning center-block price-btn"> | |
Shear Me! | |
</a> | |
</div> | |
</div> | |
<div class="col-md-6"> | |
<div class="price-square price-square-yearly"> | |
<p class="pricing-length-header yearly text-center">New Zealander</p> | |
<div class="pricing-info-padding"> | |
<p class="price text-center">$199</p> | |
<p class="text-center">The perks of a Farmer Brent membership, but with a fresh bottle of conditioner and a comb each month!</p> | |
</div> | |
<a href="{{ path('order_add_subscription_to_cart', { | |
'planId': 'new_zealander_monthly' | |
}) }}" class="btn btn-warning center-block price-btn"> | |
Get Shearing! | |
</a> | |
</div> | |
</div> | |
</div> | |
// ... lines 43 - 102 | |
</div> | |
{% endblock %} |
Go back to that page and refresh. The links look great!
Put the Plan in the Cart
Now back to the controller! In the first Stripe tutorial, we worked with a ShoppingCart
class that I created for us... because it's not really that important. It basically
allows you to store products and a subscription in the user's session, so that as
they surf around, we know what they have in their cart.
But before we use that, first get an instance of our new SubscriptionHelper
object
with $subscriptionHelper = $this->get('subscription_helper')
:
// ... lines 1 - 11 | |
class OrderController extends BaseController | |
{ | |
// ... lines 14 - 30 | |
public function addSubscriptionToCartAction($planId) | |
{ | |
$subscriptionHelper = $this->get('subscription_helper'); | |
// ... lines 34 - 42 | |
} | |
// ... lines 44 - 105 | |
} | |
// ... lines 107 - 108 |
I already registered this as a service in Symfony:
// ... lines 1 - 5 | |
services: | |
// ... lines 7 - 15 | |
subscription_helper: | |
class: AppBundle\Subscription\SubscriptionHelper | |
autowire: true |
Next, add $plan = $subscriptionHelper->findPlan()
and pass it the $planId
:
// ... lines 1 - 11 | |
class OrderController extends BaseController | |
{ | |
// ... lines 14 - 30 | |
public function addSubscriptionToCartAction($planId) | |
{ | |
$subscriptionHelper = $this->get('subscription_helper'); | |
$plan = $subscriptionHelper->findPlan($planId); | |
// ... lines 35 - 42 | |
} | |
// ... lines 44 - 105 | |
} | |
// ... lines 107 - 108 |
So this is nice: we give it the plan ID, and it gives us the corresponding, wonderful,
SubscriptionPlan
object:
// ... lines 1 - 4 | |
class SubscriptionHelper | |
{ | |
// ... lines 7 - 24 | |
/** | |
* @param $planId | |
* @return SubscriptionPlan|null | |
*/ | |
public function findPlan($planId) | |
{ | |
foreach ($this->plans as $plan) { | |
if ($plan->getPlanId() == $planId) { | |
return $plan; | |
} | |
} | |
} | |
} |
But if the $planId
doesn't exist for some reason, throw $this->createNotFoundException()
to cause a 404 page:
// ... lines 1 - 11 | |
class OrderController extends BaseController | |
{ | |
// ... lines 14 - 30 | |
public function addSubscriptionToCartAction($planId) | |
{ | |
$subscriptionHelper = $this->get('subscription_helper'); | |
$plan = $subscriptionHelper->findPlan($planId); | |
if (!$plan) { | |
throw $this->createNotFoundException('Bad plan id!'); | |
} | |
// ... lines 39 - 42 | |
} | |
// ... lines 44 - 105 | |
} | |
Finally, add the plan to the cart, with $this->get('shopping_cart')->addSubscription()
and pass it the plan ID:
// ... lines 1 - 11 | |
class OrderController extends BaseController | |
{ | |
// ... lines 14 - 30 | |
public function addSubscriptionToCartAction($planId) | |
{ | |
$subscriptionHelper = $this->get('subscription_helper'); | |
$plan = $subscriptionHelper->findPlan($planId); | |
if (!$plan) { | |
throw $this->createNotFoundException('Bad plan id!'); | |
} | |
$this->get('shopping_cart')->addSubscription($planId); | |
// ... lines 41 - 42 | |
} | |
// ... lines 44 - 105 | |
} | |
// ... lines 107 - 108 |
And boom! Our cart knows about the subscription! Finally, send them to the checkout
page with return $this->redirectToRoute('order_checkout')
- that's the name of
our checkoutAction
route:
// ... lines 1 - 11 | |
class OrderController extends BaseController | |
{ | |
// ... lines 14 - 30 | |
public function addSubscriptionToCartAction($planId) | |
{ | |
$subscriptionHelper = $this->get('subscription_helper'); | |
$plan = $subscriptionHelper->findPlan($planId); | |
if (!$plan) { | |
throw $this->createNotFoundException('Bad plan id!'); | |
} | |
$this->get('shopping_cart')->addSubscription($planId); | |
return $this->redirectToRoute('order_checkout'); | |
} | |
// ... lines 44 - 105 | |
} | |
// ... lines 107 - 108 |
Adding the Subscription on the Checkout Page
Okay team, give it a try! Add the Farmer Brent plan. Bah! We need to login: use
the pre-filled email and the password used by all sheep: breakingbaad
.
Ok, this looks kinda right: the total is $99
because the ShoppingCart
object
knows about the subscription... but we haven't printed anything about the subscription
in the cart table. So it looks weird.
Let's get this looking right: open the order/checkout.html.twig
template and scroll
down to the checkout table. We loop over the products and show the total, but never
print anything about the subscription. Add a new if
near the bottom: if cart
-
which is the ShoppingCart
object - if cart.subscriptionPlan
- which will be
a SubscriptionPlan
object or null
:
// ... lines 1 - 59 | |
{% block body %} | |
<div class="nav-space-checkout"> | |
<div class="container"> | |
<div class="row"> | |
// ... lines 64 - 66 | |
<div class="col-xs-12 col-sm-6"> | |
<table class="table table-bordered"> | |
// ... lines 69 - 74 | |
<tbody> | |
// ... lines 76 - 82 | |
{% if cart.subscriptionPlan %} | |
// ... lines 84 - 87 | |
{% endif %} | |
</tbody> | |
// ... lines 90 - 95 | |
</table> | |
</div> | |
// ... lines 98 - 100 | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Then copy the <tr>
from above and paste it here. Print out cart.subscriptionPlan.name
:
// ... lines 1 - 59 | |
{% block body %} | |
<div class="nav-space-checkout"> | |
<div class="container"> | |
<div class="row"> | |
// ... lines 64 - 66 | |
<div class="col-xs-12 col-sm-6"> | |
<table class="table table-bordered"> | |
// ... lines 69 - 74 | |
<tbody> | |
// ... lines 76 - 82 | |
{% if cart.subscriptionPlan %} | |
<tr> | |
<th class="col-xs-6 checkout-product-name">Subscription: {{ cart.subscriptionPlan.name }}</th> | |
// ... line 86 | |
</tr> | |
{% endif %} | |
</tbody> | |
// ... lines 90 - 95 | |
</table> | |
</div> | |
// ... lines 98 - 100 | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
That's why having this SubscriptionPlan
object with all of those fields is really handy.
Below, use cart.subscriptionPlan.price
and add / month
. And, whoops - I meant
to use name
on the first part, not price:
// ... lines 1 - 59 | |
{% block body %} | |
<div class="nav-space-checkout"> | |
<div class="container"> | |
<div class="row"> | |
// ... lines 64 - 66 | |
<div class="col-xs-12 col-sm-6"> | |
<table class="table table-bordered"> | |
// ... lines 69 - 74 | |
<tbody> | |
// ... lines 76 - 82 | |
{% if cart.subscriptionPlan %} | |
<tr> | |
<th class="col-xs-6 checkout-product-name">Subscription: {{ cart.subscriptionPlan.name }}</th> | |
<td class="col-xs-3">${{ cart.subscriptionPlan.price }} / month</td> | |
</tr> | |
{% endif %} | |
</tbody> | |
// ... lines 90 - 95 | |
</table> | |
</div> | |
// ... lines 98 - 100 | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Let's give it a try now. It looks great! The plans are in Stripe, the plans are in our code, and you can add a plan to the cart. Time to checkout and create our first subscription.
Hi. $this->get('shopping_cart')->addSubscription($planId) method not exist.Pleas get this method. under comment or emai.