Added request throttling to stay under the Stripe Search API rate limits (#17393)

refs https://github.com/TryGhost/Product/issues/3593

- also uses `.current_period_end` instead of `.created_at` to retrieve
the most recent subscription, when multiple customer with the same email
address are found. Reason: `created_at` date is reset when migrating
subscriptions between Stripe accounts
This commit is contained in:
Sag 2023-07-18 11:41:42 +02:00 committed by GitHub
parent 1de6120b55
commit daa19e06b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 14 additions and 6 deletions

View File

@ -2,7 +2,13 @@ const {VersionMismatchError} = require('@tryghost/errors');
const debug = require('@tryghost/debug')('stripe');
const Stripe = require('stripe').Stripe;
const LeakyBucket = require('leaky-bucket');
/* Stripe has the following rate limits:
* - For most APIs, 100 read requests per second in live mode, 25 read requests per second in test mode
* - For search, 20 requests per second in both live and test modes
*/
const EXPECTED_API_EFFICIENCY = 0.95;
const EXPECTED_SEARCH_API_EFFICIENCY = 0.15;
const STRIPE_API_VERSION = '2020-08-27';
@ -70,6 +76,7 @@ module.exports = class StripeAPI {
} else {
this._rateLimitBucket = new LeakyBucket(EXPECTED_API_EFFICIENCY * 100, 1);
}
this._searchRateLimitBucket = new LeakyBucket(EXPECTED_SEARCH_API_EFFICIENCY * 100, 1);
this._configured = true;
}
@ -230,6 +237,7 @@ module.exports = class StripeAPI {
* @returns {Promise<string|null>} Stripe Customer ID, if found
*/
async getCustomerIdByEmail(email) {
await this._searchRateLimitBucket.throttle();
try {
const result = await this._stripe.customers.search({
query: `email:"${email}"`,
@ -261,8 +269,8 @@ module.exports = class StripeAPI {
// find the customer with the most recent subscription
for (let subscription of customer.subscriptions.data) {
if (subscription.created > latestSubscriptionTime) {
latestSubscriptionTime = subscription.created;
if (subscription.current_period_end && subscription.current_period_end > latestSubscriptionTime) {
latestSubscriptionTime = subscription.current_period_end;
latestCustomer = customer;
}
}
@ -420,7 +428,7 @@ module.exports = class StripeAPI {
const metadata = options.metadata || undefined;
const customerId = customer ? customer.id : undefined;
const customerEmail = customer ? customer.email : options.customerEmail;
await this._rateLimitBucket.throttle();
let discounts;
if (options.coupon) {

View File

@ -209,8 +209,8 @@ describe('StripeAPI', function () {
id: 'recent_customer_id',
subscriptions: {
data: [
{created: 1000},
{created: 9000}
{current_period_end: 1000},
{current_period_end: 9000}
]
}
},
@ -224,7 +224,7 @@ describe('StripeAPI', function () {
id: 'old_customer_id',
subscriptions: {
data: [
{created: 5000}
{current_period_end: 5000}
]
}
}