#roda_api

1 messages ยท Page 1 of 1 (latest)

vocal ironBOT
#

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

๐Ÿ“ Have more to share? Add more details, code, screenshots, videos, etc. below.

lone urchin
#

Hi! Email is not globally unique at Stripe, so this will happen. Your best bet is to store the [ Customer ID, email ] relationship so you can pull the correct customer given an email.

rapid moat
#

I also sent a request to Stripe, because there is a issue involved with this, because when this happens the old customer portal becames inacessible and could have an active subscription in it. The current behaviour retrieves the most recent customer_id created from a certain email.

But about the API, there isn't any endpoint to retrieve all subscriptions for a certain email right?

lone urchin
rapid moat
#

I was talking about retrieve all subscriptions

lone urchin
#

You can't search Subscription by email so you'd have to search Customer first, and then get their Subscriptions.

rapid moat
#

 const customerIds = await stripe_1.resolveCustomerIdFromEmail(customer.email);

        // Collect all subscriptions from all customer IDs
        const subscriptions = await Promise.all(
            customerIds.map(async (cId) => {
                return await stripe_1.findSubscriptionsFromCustomerId(cId);
            })
        );
        const allSubscriptions = subscriptions.flat();

Currently doing this way, so probably the only way to do it in Code if there isn't any filter to get it all for the same email address.

lone urchin
#

Those function calls on stripe_1 are not part of the Stripe Node SDK so I'm not sure what they do.

rapid moat
#

const resolveCustomerIdFromEmail = async (email) => {
    let matchingCustomers = [];

    const response = await fetch(`https://api.stripe.com/v1/customers/search?query=email:'${email}'`, {
            method: 'GET',
            headers: {
                Authorization: `Bearer ${process.env.STRIPE_API_KEY}`
            }
        });

    const responseData = await response.json();
        matchingCustomers = responseData.data || [];
   }

    // Return an array of customer IDs
    return matchingCustomers.map(customer => customer.id).filter(Boolean);
}
exports.resolveCustomerIdFromEmail = resolveCustomerIdFromEmail;

/**
 * Gets all the Stripe subscriptions from a given customer ID
 */
const findSubscriptionsFromCustomerId = async (customerId) => {
    const response = await fetch(`https://api.stripe.com/v1/subscriptions?customer=${customerId}`, {
        method: 'GET',
        headers: {
            Authorization: `Bearer ${process.env.STRIPE_API_KEY}`
        }
    });

    const responseData = await response.json();
    return responseData.data || [];
}
exports.findSubscriptionsFromCustomerId = findSubscriptionsFromCustomerId;
#

Just searching form the email, and then, the subscriptions for the customerId.

#

I think with the current endpoints available this is the only way

vocal ironBOT
lone urchin
rapid moat
lone urchin
#

Give me a sec.

lone urchin
#

Ok, here you go:

#

You can replace your two-step, many-request process with a single API request:

// const resolveCustomerIdFromEmail = async (email) => {
const getSubscriptionsForEmail = async (email) => {
  let subscriptions = [];

  const response = await fetch(
    `https://api.stripe.com/v1/customers/search?query=email:'${email}'&expand[]=data.subscriptions&expand[]=data.subscriptions.data.customer`,
    {
      method: "GET",
      headers: {
        Authorization: `Bearer ${process.env.STRIPE_API_KEY}`,
      },
    }
  );

  const customers = await response.json();

  return customers.data
    .map((customer) => customer.subscriptions.data)
    .flat()
    .filter(Boolean);
};
#

And now I must away. โœŒ

rapid moat
#

Wow awesome!

vocal ironBOT
lone urchin
#

(My colleague solanum is however here to help if you need anything further. ๐Ÿ™‚ )

rapid moat
lone urchin
#

Yup.

#

You have to initialize stripe but this what it'd look like.

const getSubscriptionsForEmail = async (email) => {
  const customers = await stripe.customers.search({
    query: `email:'${email}'`,
    expand: ["data.subscriptions"],
  });

  return customers.data
    .map((customer) => customer.subscriptions.data)
    .flat()
    .filter(Boolean);
};
rapid moat
#

Got it, thanks timebox! Go to your well deserved day off :P >.<

lone urchin
#

If you also want to expand the Customer object expanded on the Subscription (i.e. subscrtiption.customer, you can use data.subscriptions.data.customer as the expand.

#

Will do. Cheers. ๐Ÿ™‚

rapid moat
#

Hey solanum, I have a question for you, should I add a sleep cycle to avoid rate limiting on this? And if so how should I do this implementation about the timing?

tulip oriole
#

ooo hello! just noticed the followup, gimme just a second

#

what context are you doing this in? are you writing production code? if so you want retries with exponential backoff

rapid moat
#

I do have an open source solution to sync roles with subscriptions in stripe, and I'm currently checking with some hour intervals (I know I can do this with webhooks now, but for now I'll stay with intervals checks)

And when the interval checks are made I loop all the customers linked with discord to make sure every single one has a subscription active, if it' isn't i revoke roles

tulip oriole
#

ooo yep, i will say that yes webhooks are greatly preferred here. but yeah i would just implement retries as described in the article above

#

obviously just adding an arbitrary sleep will reduce chances of 429s, but retries with exponential backoff will ensure you don't miss anything

rapid moat
#

But should I add a sleep of fractions of seconds when looping each customer to avoid the rate limit, or?

tulip oriole
#

sure! again that will help, but retries are more important

rapid moat
#

I started my version of this bot two years ago and I was doing


const sleep = async (ms) => await new Promise(resolve => setTimeout(resolve, ms));

await sleep(2000);

#

Maybe I don't need 2 full seconds right

#

๐Ÿ˜…

tulip oriole
#

yeah that's a lot hahahaha

rapid moat
#

Awesome! Maybe you have any recommendation value? ๐Ÿ˜†

rapid moat
tulip oriole
#

the limit is this: Live mode: 100 read operations and 100 write operations

rapid moat
tulip oriole
#

so even 1/100th of a second will guarantee you don't hit the limit (unless your application is making other calls at the same time)