Stripe's new Price API and how use-shopping-cart helps you get to Checkout
Recently, it was brought to my attention that someone tried out use-shopping-cart and was having a hard time. It turns out that USC, by default, leads people to hit Stripe’s Checkout with a deprecated way of creating Session IDs.
I wanted to fix this ASAP, so I started doing homework. This blog post is about what I’ve learned and what use-shopping-cart will help with if you decide to use this for your e-commerce store.
The Three Scenarios for Checkout
There are 3 main ways we’d see developers using USC to get to Stripe’s Checkout. The two easiest ways would be:
- You made products on Stripe’s dashboard using the Price API. Price ids have a
price_
prefix - You made products on Stripe’s dashboard a long time ago on the SKU API, before Price API existed. SKU ids have a
sku_
prefix.
The more complicated scenario is:
- Your product data is coming from a source that isn’t Stripe, but you’d like to use Stripe to handle your transactions.
What makes the first two scenarios easy is that Stripe holds most of the information you need when passing these products to the Session API (to create Session IDs, you need Session IDs to get to Checkout).
Let’s take a look at what creating a sessionID should look like:
const stripe = require('stripe')('sk_test_YOUR_SECRET_API_KEY');const session = await stripe.checkout.sessions.create({success_url: 'https://example.com/success',cancel_url: 'https://example.com/cancel',payment_method_types: ['card'],line_items: [{price: 'price_H5ggYwtDq4fbrJ', quantity: 2},],mode: 'payment',});
This is straight from the Stripe docs themselves. You see line_items
? This key is where you pass the price ID of the product you want to sell. The only other key you need next to it the quantity. Stripe would know what to do from there as far as images, costs etc.
The significance here is that you’re using Stripe on your backend to handle and secure your payment. You don’t even need a server to go through this experience!
Stripe has a function called redirectToCheckout
that allows you to get to Checkout. use-shopping-cart has its own version of redirectToCheckout
that formats your shopping cart data for you to fit what line_items
needs.
However, for bigger e-commerce stores, you might want to use Stripe’s dashboard to create your products and still need a server to add extra configuration to your payments.
With the previous version of use-shopping-cart, the only way to get the proper format for line_items was through validateCartItems
(I’ll talk more about this later).
For this upcoming release, I’ve made a function called formatLineItems
. All it does is help you format your data correctly, it’s very small and cute:
const formatLineItems = (cartDetails) => {const lineItems = []for (const itemId in cartDetails) {if (cartDetails[itemId].sku_id || cartDetails[itemId].price_id)lineItems.push({ price: itemId, quantity: cartDetails[itemId].quantity })}return lineItems}
Note that it looks for either price_id
or sku_id
Now let’s move on to the juicy stuff!
I said earlier that the API use-shopping-cart was using to create sessions is deprecated. To understand what use-shopping-cart does for you under the hood, I think it makes sense to take a look at how Stripe currently recommends making your products:
You have Products and Prices:
One product can have many prices, but prices can only have one product. You have to create a Product and a Price for that product at the same time. Maybe you want to make a one-time sale but also provide an option for subscriptions as well, or maybe different variations of a product is worth more or less. This is what I think Stripe’s set up is allowing us to manage.
Let’s take a look at the deprecated way of creating session ids. What we’re really talking about, though, is line_items
:
{name: Sun Glasses,amount: 500,currency: 'USD',quantity: 2}
SPECULATION
I think the reason why Stripe is deprecating this format for line_items
is because it doesn’t really seperate Product and Price the way they have you do it when you use their dashboard. Product and Price are two seperate entities to Stripe, this structure doesn’t communicate that very well.
Stripe doesn’t really document the way you’re supposed to use the Price API without their dashboard. In the docs, you’ll see a string that says CONDITIONALLY REQUIRED
.
What conditions had to be met that determined what was required? This is what I spent my time figuring out, so use-shopping-cart can be useful.
Using the new Price API
Here is the final format you need to create a session id with the Price API:
{price_data: {currency: "usd",unit_amount: 500,product_data: {name: "Sun Glasses"}},quantity: 2}
The thing that is “conditionally required” is whether or not you’re using the Price API. If you’re not using the Price API, you don’t need to adhere to this format. You can see that it’s the same data as before, however, they made a few adjustments:
price_data
Remember I said earlier that the deprecated format doesn’t communicate what is price
and what is product
. This is Stripe’s way of adjusting that. There is a literal price_data
key now, where you pass in data specifically about the Price.
product_data
Same thing goes for this object, at the bare minimum, you need the name of the product. A lot more can go here if you needed it. Keep in mind that product_data
is a child of price_data
How use-shopping-cart helps
The previous version of use-shopping-cart (assuming you’re reading this after the release 😅) only handled the case for the old way in a function called validateCartItems
.
You need validateCartItems
for security reasons. If someone edited the prices of your products on the DOM using Inspect Element, they could send fake prices to your server and basically rob you! validateCartItems
takes in your inventory source as the first param, and your current cart as the second param, checks against them and returns the right prices for each product, and then formats them appropriately to line_items
.
I was debating on whether or not we should have people create products the same way as Stripe does, with the separate price and product data, but I decided it was easy enough to infer what should go where.
You can continue to create your product data like this:
[{"name": "Bananas","id": "test1","price": 400,"image": "https://i.imgur.com/AUJQtJC.jpg","currency": "USD"},{"name": "Tangerines","id": "test2","price": 100,"image": "https://i.imgur.com/4rVhatT.jpg","currency": "USD"}]
Some things to note:
use-shopping-cart was built, probably at the worst possible time (lol). It was just as the SKU API was on its way out, so a lot of stuff under the hood had that API in mind. This means that anyone who might have started using this early, prob have their data with sku
keys that are “acting as id
” in the object above.
The good news is, we’re providing ways to maintain that structure so you don’t have to change much. sku
and id
are interchangeable, but we’d prefer if you used id
.
As far as using Stripe dashboard products, you should pass those ids as price_id
. We also look for a sku_id
key, but I just learned that you can actually pass sku
to the price
key in line_items
like 30 mins before I started writing this.
That’s it! I have been working on this for about a week and making tons of tweaks to use-shopping-cart to get this working. Hopefully, you found this post helpful/interesting.
If anything I said about how Stripe is doing things is wrong, please let me know!