#yopassit_api
1 messages ยท Page 1 of 1 (latest)
๐ Welcome to your new thread!
โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.
โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.
๐ This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1314399201156792382
๐ Have more to share? Add more details, code, screenshots, videos, etc. below.
Some additional information:
Companies are the entities paying for the entirety of the bill, therefore I should create a Stripe Customer per Company creation correct?
Subscriptions are related to Products (correct?) therefore I would need to also create a Stripe Product, and a Stripe Subscription per company as well?
When a company onboards a user, I should update the Stripe Subscription price by $20 dollars correct.
When a company remove a user, I should dock it by $20, but only on the next billing cycle.
Hi, I am in a MTG and will be here soon. Sorry for the waiting time!
Sorry back and catching up...
I charge a minimum plan of $320 a month.
This comes with:
- 12 seats
Every additional seat costs an additional $20
Companies will have a variadic amount of seats, some may have just the 16 allocated, others will have 30+.
This sounds like the exact usage of Tiered pricing and volumn-based pricing. I would recommend looking through https://docs.stripe.com/products-prices/pricing-models?dashboard-or-api=api#tiered-pricing and testing on Test mode. Should be doable here
Companies are the entities paying for the entirety of the bill, therefore I should create a Stripe Customer per Company creation correct?
Yes, one Customer object representing one company
Subscriptions are related to Products (correct?) therefore I would need to also create a Stripe Product, and a Stripe Subscription per company as well?
You only need to create 1 Product and 1 Price, then use it to multiple Customers, creating multiple Subscriptions. Each Customer has 1 Subscription in your case
When a company onboards a user, I should update the Stripe Subscription price by $20 dollars correct.
When a company remove a user, I should dock it by $20, but only on the next billing cycle.
You will modify the volume usage (see the Doc above) to reflect that. By default quantity is reflected immediately and be counted at end of period. If you only want to reduce it for the future billing cycle, you may want to "schedule" that change, either using your cronjob or use the Subscription Schedule API. But this API requires a bit more efforts so I would recoomend do it last :). Utilize our Test mode and Billing Test Clock to test things
Okay, so let's say I onboard a company, this will be manual through an Admin page of my SaaS. I will then get the user to log into my app, then from there I should implement a way for them to attach a payment method correct?
Yes, you should build a page to collect their payment info. There are multiple ways but easiest is redirect to a Stripe hosted page. It's call Stripe Checkout
You can start here https://docs.stripe.com/billing/subscriptions/integration
Okay, do I need separate pages for attaching payments and subscribing or can it be done in one?
No it will be done in backend
If you look at the integration, it create a Subscription on backend first, then use a generated client secret (relates to that Subscription) and collect payment methods on frontend. After the customer keys-in their payment method, it will automatically linked to the Subscription
the client secret is what exactly? A secure cookie that is stored on the client, or is it something else.
or its stored in the url that i redirect the user to?
No it's a value generated by Stripe API in backend. Sorry it is on an advance flow. If you are using a simpler flow only redirect to a Stripe hosted Checkout page, you wouldn't need it. Lets look here first: https://docs.stripe.com/billing/subscriptions/build-subscriptions?platform=web&ui=stripe-hosted
Here you generate a Checkout Session on backend, and simply takes its URL and redirect on browser
we take care of the rest
Okay, so the product is something i create manually and once. Like via your dashboard
Then when a company is created I create a customer and store the customer id in my database pointing to the company it belongs to
Yep
Then I foward the user to a stripe hosted frontend so they can securely subscribe to it.
Then every time I accept a new user I change the volume?
If I'm removing a user, use cron job or Stripe Scheduler to handle the volume dismount
This is what my tiering should look like correct?
Actually no its not, the flat fee needs to be removed as it adds 320 + 17 * 20
But other than that thanks.
In terms of non Stripe related processes, how do I manage this on my backend.
Should companies table have a collumn called active with a boolean?
Then through webhooks I change or unchange that boolean?
That's ... totally up to you, but I think you want to store the Stripe Subscription Id (sub_xxx) and Stripe Customer Id (cus_xxx). Customer Id is mapped 1:1 to your companies database
The Subscription ID will never change right?
Unless they cancel it and create and entirely new one? Or should I continue the old one?
okay yes.
Tell me more about the flow of the Stripe hosted frontends
How do I securely send the user to your Stripe Frontend to manage the subscription
- Your frontend sends a request to your backend
- Your backend send a request to Stripe to create a Checkout Session
- Your backend got the result, take its URL, and tell your frontend to redirect the browser
Okay like OAuth
Not really OAuth. See this step: https://docs.stripe.com/billing/quickstart#redirect
ie. in Ruby
redirect session.url, 303
yes I understand
Are webhooks the only way to get notified of successful and unsuccessful payments to the subscription?
Not the only way but recommended
If my server goes down or something how will i persist the webhook
is there retries or will I get an email or something?
If you don't respond 200 to the webhook event, we will retry
There is retry policy
But baiscally you would want to keep your server alive to receive webhook events from Stripe, or setup 2 different endpoints, listen to the same set of events
Do you know Golang? So I can show code?
Sure
package billing
import (
"github.com/stripe/stripe-go/v81"
"github.com/stripe/stripe-go/v81/client"
"noted/config"
)
type StripeBilling struct {
client *client.API
}
func NewStripeBilling(cfg *config.Config) *StripeBilling {
return &StripeBilling{client: client.New(cfg.StripeAPIKey, nil)}
}
type CreateCustomerParams struct {
CompanyName string
CompanyID string
Email string
Name string
Phone string
City string
Country string
Line1 string
Line2 string
PostalCode string
State string
}
func (s *StripeBilling) CreateCustomer(p *CreateCustomerParams) (*stripe.Customer, error) {
params := &stripe.CustomerParams{
Email: &p.Email,
Name: &p.Name,
Phone: &p.Phone,
Address: &stripe.AddressParams{
City: &p.City,
Country: &p.Country,
Line1: &p.Line1,
Line2: &p.Line2,
PostalCode: &p.PostalCode,
State: &p.State,
},
Metadata: map[string]string{
"company_id": p.CompanyID,
"company_name": p.CompanyName,
},
}
return s.client.Customers.New(params)
}
I do this on creation of a Company.
Now for the product, I created it manually via your Dashboard
So I would need to inject the ID of it as a environment variable?
You can also create Product on code, but your choice
well if its a one and done it would make it complicated
You may want to save the Product Id in database one time and retrive, instead of an environment variable
Does Stripe have a Product finder method
like a find by name or something so I can just run that at the start of the server and store it in memory
You can call Search Product API, but better to save its Id on your database
Probably slap it in redis key/value store
I'm still confused on the flow
I create a subscription with the client on the backend like this?:
s.client.Subscriptions.New(params)
And then it suppose to give me something to redirect the user with?
Nope, you don't create Subscription
you create a Checkout Session, with mode = "Subscription"
and I have to pass the product to this Checkout Session
Pass the Price Id actually
https://docs.stripe.com/billing/quickstart?lang=go#redirect
checkoutParams := &stripe.CheckoutSessionParams{
Mode: stripe.String(string(stripe.CheckoutSessionModeSubscription)),
LineItems: []*stripe.CheckoutSessionLineItemParams{
&stripe.CheckoutSessionLineItemParams{
Price: stripe.String(price.ID),
Quantity: stripe.Int64(1),
},
},
SuccessURL: stripe.String(domain + "/success.html?session_id={CHECKOUT_SESSION_ID}"),
CancelURL: stripe.String(domain + "/cancel.html"),
}
s, err := session.New(checkoutParams)
so the price id matters not the product ID?
So via this Checkout Session, the user manages these:
- Payments Methods
- Subscription Cancellation
- Subscription Subscribing
No they will only add the Payment Method
You will manage the Subscription, unless you provide a way for them to manage the cancellation themselves
There isn't a Screen like the Checkout Session for Canceling/Subscribing to a Subscription?
There is. It's called the Billing Portal. Similar that you create a Session and then redirect your customer to it
so if checkout session is only for managing payment methods, why does it need a priceID?
The Stripe Docs state that through the Billing Portal they can manage their payment methods, so shouldn't I just use that and not the checkout session
Checkout Session is to collect the payment method first time, then you can activate the Subscription.
After that, Billing Portal is for your customer to manage the activated Subscription: either change it, or cancel it
There are 2 different processes
Okay, so when they first subscription is checkout session
then on managing it is with billing portal
Current flow is:
- On Company Creation, create a Stripe Customer and store it relationally in the database
- Then in SaaS Frontend, have a Subscribe button that will create a Checkout Session so the user can add Payment Methods and Subscribe for the first time. (use webhooks to acknowledge success??)
- Then on, when the user wants to change payment methods or update subscriptions, use Billing Portal.
Now the next thing is changing the volume of the subscription when the user adds seats and removes them
Yes, correct
And with the tiering is it automatic based on the users
Like when I change volume to tier 2 amount of uses it will start doing $20 a seat
Yep
So now how do I manipulate the subscription volume? I would assume I need the Subscription ID and store it in my database pointing to the company.
Where do I get the Subscription ID? From the Webhook?
Yes from the webhook, that you receive customer.subscription.created after the Sub is created
When a user cancels it do I receive the webhook immediately or do I receive it when the subscription period ends?
So in my backend I would need to have a system in place that will set the company's active state to false after the subscription period ends?
Well if a user cancels early in the month I don't immediately want to revoke their access if they still payed
Well you can call the Cancel Subscription API by yourself and pass cancel_at_period_end to true
https://docs.stripe.com/billing/subscriptions/cancel#cancel-at-the-end-of-the-current-billing-cycle exactly here
okay but how would I prevent them from doing it in the Billing Portal
Unless i scrap the billing portal
Use a configuration
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
set canceL_mode to at_period_end here
func (s *StripeBilling) CreateBillingPortalSession(customerID string) (*stripe.BillingPortalSession, error) {
params := &stripe.BillingPortalSessionParams{
Customer: &customerID,
ReturnURL: stripe.String(s.config.FrontendURL + "/dashboard"),
}
return s.client.BillingPortalSessions.New(params)
}
How would you do it here?
Configuration: sripe.String(string(stripe.BillingPortalConfigurationFeaturesSubscriptionCancelModeAtPeriodEnd)),
Like this right
I imagine configuration is a separated object
you create the Configuration object first, then create the Billing Portal with that config
I thought maybe the stripe.BillingPortalConfiguration{} would have a .String() but it doesn't
It takes the Id of the Configuration object
Id is a string
you create the Configuation object first, then grab its Id
So I use the API to create a confuration object then pass the ID?
But i guess I only need to do it one and done again right
Is this something I can make through the Dashboard?
params := &stripe.BillingPortalConfigurationParams{
Active: stripe.Bool(true),
Features: &stripe.BillingPortalConfigurationFeaturesParams{
SubscriptionCancel: &stripe.BillingPortalConfigurationFeaturesSubscriptionCancelParams{
Mode: stripe.String(string(stripe.BillingPortalConfigurationFeaturesSubscriptionCancelModeAtPeriodEnd)),
},
},
}
I think actually I'm going to leave it defaulted so the users can cancel in the portal. I figure out a work around later.
func (s *StripeBilling) DecrementVolumeSubscription(subscriptionID string) error {
subscription, err := s.client.Subscriptions.Get(subscriptionID, nil)
if err != nil {
s.logger.Error("failed to get subscription", err)
return err
}
if len(subscription.Items.Data) == 0 {
return fmt.Errorf("no subscription items found")
}
item := subscription.Items.Data[0]
currentQuantity := item.Quantity
if currentQuantity <= 1 {
return fmt.Errorf("cannot decrease quantity below 1")
}
newQuantity := currentQuantity - 1
params := &stripe.SubscriptionItemParams{
Quantity: stripe.Int64(newQuantity),
}
_, err = s.client.SubscriptionItems.Update(item.ID, params)
return err
}
As for decrementing I have this... How can I use the Scheduler to do it a month later?
That is more complicated and I would recommend the Billing Portal + Configuration over it
Subscription Schedule is a different API
Well I need a way to handle it at the next billing cycle because they could just remove all the users last second and get charged nothing right?
I could do it with a cron job and a database table
Yes a cronjob works too, but you will need to manage it ๐
Yeah maybe have a table that will detail the start of the billing period and when to remove a user and run it daily