42
loading...
This website collects cookies to deliver better user experience
v1/payment_methods?customer={customerId}
endpoint (and a little formatting) away.available
for each user. So as a user searches for content, the results consider a user's purchase and subscription history by replacing a Buy Now
button with a Watch Now
button.available
if the user has an active subscription including that movieavailable
if the user has purchased itavailable
flag should be immediately updated when:
movie_search
:movie_id | subscription_groups | title | keywords |
---|---|---|---|
1 |
action , adventure
|
Indiana Jones | 'harrison ford action archaeologist' |
2 |
action , sci-fi
|
Star Wars | 'harrison ford space opera' |
tsvector
and tsquery
for a simple Full Text Search interface. For example, the following sql clause will match any rows where the keywords
field contains both harrison
and ford
:to_tsvector(movie.keywords) @@ to_tsquery('harrison & ford')
included_in_user_subscription
) might look something like this:def get_movie_results(customer_id, query):
# somehow fetch the users's active subscriptions
user_subscribed_groups = get_subscription_groups_for_customer(customer_id)
query = """
select
movie_search.movie_id,
movie_search.title,
-- We'll use postgres' '&&' (overlap) operator to check if the user's subscriptions
-- include any of the subscription_groups for the movie.
-- The users's 'subscription_groups' are parameterized here as '$1'
(movie.subscription_groups && $1) as included_in_user_subscription
from movie_search
where to_tsvector(movie.keywords) @@ to_tsquery($2)
"""
return db.execute(query, [subscription_groups, query])
/v1/subscriptions?customer=<customerId>
endpoint. The response includes subscriptionItems
as a list (which may require separate pagination calls) which each contain a reference to a product
.productId
s from the subscriptionItems
you could now use /v1/products?id=<productId1>,<productId2>,<productId3>
(again, requiring pagination if you have a lot of productIds) to get that metadata[1], which you can then merge into the results from your movie search system to mark movies which are included in active subscriptions as available.[1] Depending on your Stripe integration, a Stripe ProductId could map to a number of different objects in your system such as a specific Movie
, a Genre
, a Movie Studio
or something like New Releases
. There are multiple ways you may be storing the relationship between Stripe's concept of a product and your own, but a common pattern is to store this on the product.metadata
field.
auto_paging_iter
helper method that handles pagination. Note: this method may use as few as 2 api calls, but any pagination required will still count towards Stripe's API rate limits of 100 requests per second.def get_customer_subscription_groups(customer_id: str):
subscribed_product_ids = set()
subscriptions = stripe.Subscriptions.list(customer=customer_id)
for subscription in subscriptions.auto_paging_iter():
for subscription_item in subscription.items.auto_paging_iter():
product_id = subscription_item.price.product
subscribed_product_ids.add(product_id)
subscription_groups = set()
products = stripe.Products.list(ids=list(subscribed_product_ids))
# for each product, extract the 'subscription_group' and add it to our set
for product in products.auto_paging_iter():
metadata = product.metadata
subscription_group = metadata.get('subscription_group')
subscription_groups.add(subscription_group)
return list(subscription_groups)
stripe.subscription
, stripe.subscription_items
and stripe.product
tables which contain the same data as you'd get from the Stripe API. The approach is similar to Including Subscriptions In The Search Query, outlined above, but with the added benefit of the user's subscription database being pulled directly from the same database as part of the query.select
movie_search.movie_id,
exists(
select 1
from stripe.subscription s
join stripe.subscription_item si
on s.id = si.subscription_id
join stripe.product p
on si.product_id = p.id
where s.customer_id = '<Stripe_customer_id>'
and p.metadata -> 'group' = ANY(movie.subscription_groups)
) as included_in_subscription
from movie_search
where to_tsvector(move.info) @@ to_tsquery('harrison & ford')
prod_0001
would trigger an HTTP POST request to your API with contents like:{
"object": "event",
"type": "customer.subscription.created",
...
"data": {
"object": {
"object": "subscription",
"id": "sub_0027",
"customer": "cus_0012",
...
"items": [
...
"data": [
{
"object": "subscription_item",
...
"price": {
"product": "prod_0001"
}
}
]
]
}
}
}
class Subscripton(Model):
id = fields.text()
# ... more fields
def create_subscription(customer_id: str, price: str):
subscription = stripe.Subscription.create(
customer=customer_id",
items=[
{"price": price"},
],
)
# tranform the customer object to fit your model
# and insert-and-update
self.create_from_stripe_object(subscription)
return customer
stripe.Subscription.create
will return an object similar to the WebHook JSON payload shown above, including nested objects for subscription
, subscription_item
and price
(amongst others). You could use this response in a function, something like Subscription.create_from_stripe_object
to traverse these nested objects and insert/update the corresponding records in your own database.unsubscribe
but sometimes payments are refused and bank accounts get closed - and you'll need to handle that! You'll still need webhooks or else these changes will never make it back to your app database!42