#yopassit_api

1 messages ยท Page 1 of 1 (latest)

rain rampartBOT
#

๐Ÿ‘‹ 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.

woeful pendant
#

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.

dim bough
#

Hi, I am in a MTG and will be here soon. Sorry for the waiting time!

dim bough
#

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

Learn about common pricing models and how to create them.

woeful pendant
#

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?

dim bough
#

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

woeful pendant
#

Okay, do I need separate pages for attaching payments and subscribing or can it be done in one?

dim bough
#

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

woeful pendant
#

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?

dim bough
#

Here you generate a Checkout Session on backend, and simply takes its URL and redirect on browser

#

we take care of the rest

woeful pendant
#

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

dim bough
#

Yep

woeful pendant
#

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?

dim bough
#

Yeah looks correct. Please do test it

#

Every user will have different volume, yes

woeful pendant
#

Actually no its not, the flat fee needs to be removed as it adds 320 + 17 * 20

#

But other than that thanks.

dim bough
#

Ah Flat Fee for the second tier

#

you are right

woeful pendant
#

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?

dim bough
#

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

woeful pendant
#

The Subscription ID will never change right?

dim bough
#

Yes

#

Ids won't change

woeful pendant
#

Unless they cancel it and create and entirely new one? Or should I continue the old one?

dim bough
#

In that case you will have a new Subscription object

#

still on the same Customer Id

woeful pendant
#

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

dim bough
#
  • 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
woeful pendant
#

Okay like OAuth

dim bough
#

ie. in Ruby

  redirect session.url, 303
woeful pendant
#

yes I understand

#

Are webhooks the only way to get notified of successful and unsuccessful payments to the subscription?

dim bough
#

Not the only way but recommended

woeful pendant
#

If my server goes down or something how will i persist the webhook

#

is there retries or will I get an email or something?

dim bough
#

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

woeful pendant
#

Do you know Golang? So I can show code?

dim bough
#

Sure

woeful pendant
#
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?

dim bough
#

You can also create Product on code, but your choice

woeful pendant
#

well if its a one and done it would make it complicated

dim bough
#

You may want to save the Product Id in database one time and retrive, instead of an environment variable

woeful pendant
#

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

dim bough
#

You can call Search Product API, but better to save its Id on your database

woeful pendant
#

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?

dim bough
#

Nope, you don't create Subscription

#

you create a Checkout Session, with mode = "Subscription"

woeful pendant
#

and I have to pass the product to this Checkout Session

dim bough
#

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)
woeful pendant
#

so the price id matters not the product ID?

#

So via this Checkout Session, the user manages these:

  • Payments Methods
  • Subscription Cancellation
  • Subscription Subscribing
dim bough
#

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

woeful pendant
#

There isn't a Screen like the Checkout Session for Canceling/Subscribing to a Subscription?

dim bough
#

There is. It's called the Billing Portal. Similar that you create a Session and then redirect your customer to it

woeful pendant
#

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

dim bough
#

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

woeful pendant
#

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

dim bough
#

Yes, correct

woeful pendant
#

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

dim bough
#

Yep

woeful pendant
#

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?

dim bough
#

Yes from the webhook, that you receive customer.subscription.created after the Sub is created

woeful pendant
#

When a user cancels it do I receive the webhook immediately or do I receive it when the subscription period ends?

dim bough
#

Immediately, customer.subscription.deleted

#

You can test all this using Test Clock

woeful pendant
#

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?

dim bough
#

Yes

#

Well it's more of depends on your business needs

woeful pendant
#

Well if a user cancels early in the month I don't immediately want to revoke their access if they still payed

dim bough
#

Well you can call the Cancel Subscription API by yourself and pass cancel_at_period_end to true

woeful pendant
#

okay but how would I prevent them from doing it in the Billing Portal

#

Unless i scrap the billing portal

dim bough
#

Use a configuration

#

set canceL_mode to at_period_end here

woeful pendant
#
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

dim bough
#

I imagine configuration is a separated object

#

you create the Configuration object first, then create the Billing Portal with that config

woeful pendant
#

I thought so too

#

But its a string pointer

dim bough
woeful pendant
#

I thought maybe the stripe.BillingPortalConfiguration{} would have a .String() but it doesn't

dim bough
#

It takes the Id of the Configuration object

#

Id is a string

#

you create the Configuation object first, then grab its Id

woeful pendant
#

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

dim bough
#

Yeah

#

Please see the Doc above

woeful pendant
#

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)),
        },
    },
}
dim bough
woeful pendant
#

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?

dim bough
#

That is more complicated and I would recommend the Billing Portal + Configuration over it

#

Subscription Schedule is a different API

woeful pendant
#

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

dim bough
#

Yes a cronjob works too, but you will need to manage it ๐Ÿ™‚

woeful pendant
#

Yeah maybe have a table that will detail the start of the billing period and when to remove a user and run it daily