Handled product save failure on Stripe connect

refs https://github.com/TryGhost/Team/issues/704

We try and create new default prices soon after Stripe Connect is completed, but it might take couple of seconds for backend to have Stripe config ready and in the meanwhile saving a product with new prices will fail with 409 Conflict error as it will be unable to create prices on Stripe. This change allows re-attempting saving a product with new prices soon after connect in case of a STRIPE_NOT_CONFIGURED error so that the default prices can be created properly.
This commit is contained in:
Rishabh 2021-05-24 14:02:43 +05:30 committed by Rishabh Garg
parent 8862960974
commit 740a304bf0
2 changed files with 51 additions and 4 deletions

View File

@ -5,6 +5,9 @@ import {task} from 'ember-concurrency-decorators';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';
const RETRY_PRODUCT_SAVE_POLL_LENGTH = 1000;
const RETRY_PRODUCT_SAVE_MAX_POLL = 15 * RETRY_PRODUCT_SAVE_POLL_LENGTH;
export default class GhLaunchWizardConnectStripeComponent extends Component {
@service ajax;
@service config;
@ -88,6 +91,28 @@ export default class GhLaunchWizardConnectStripeComponent extends Component {
this.settings.set('portalPlans', portalPlans);
}
@task({drop: true})
*saveProduct() {
let pollTimeout = 0;
while (pollTimeout < RETRY_PRODUCT_SAVE_MAX_POLL) {
yield timeout(RETRY_PRODUCT_SAVE_POLL_LENGTH);
try {
const updatedProduct = yield this.product.save();
return updatedProduct;
} catch (error) {
if (error.payload?.errors && error.payload.errors[0].code === 'STRIPE_NOT_CONFIGURED') {
pollTimeout += RETRY_PRODUCT_SAVE_POLL_LENGTH;
// no-op: will try saving again as stripe is not ready
continue;
} else {
throw error;
}
}
}
return this.product;
}
@task({drop: true})
*openDisconnectStripeConnectModalTask() {
this.hasActiveStripeSubscriptions = false;
@ -170,8 +195,7 @@ export default class GhLaunchWizardConnectStripeComponent extends Component {
}
);
this.product.set('stripePrices', stripePrices);
yield timeout(1000);
const updatedProduct = yield this.product.save();
const updatedProduct = yield this.saveProduct.perform();
const monthlyPrice = this.getActivePrice(updatedProduct.stripePrices, 'month', 500, 'usd');
const yearlyPrice = this.getActivePrice(updatedProduct.stripePrices, 'year', 5000, 'usd');
this.updatePortalPlans(monthlyPrice.id, yearlyPrice.id);

View File

@ -5,6 +5,9 @@ import {reads} from '@ember/object/computed';
import {inject as service} from '@ember/service';
import {task, timeout} from 'ember-concurrency';
const RETRY_PRODUCT_SAVE_POLL_LENGTH = 1000;
const RETRY_PRODUCT_SAVE_MAX_POLL = 15 * RETRY_PRODUCT_SAVE_POLL_LENGTH;
export default Component.extend({
config: service(),
ghostPaths: service(),
@ -258,6 +261,27 @@ export default Component.extend({
this.settings.set('portalPlans', portalPlans);
},
saveProduct: task(function* () {
let pollTimeout = 0;
while (pollTimeout < RETRY_PRODUCT_SAVE_MAX_POLL) {
yield timeout(RETRY_PRODUCT_SAVE_POLL_LENGTH);
try {
const updatedProduct = yield this.product.save();
return updatedProduct;
} catch (error) {
if (error.payload?.errors && error.payload.errors[0].code === 'STRIPE_NOT_CONFIGURED') {
pollTimeout += RETRY_PRODUCT_SAVE_POLL_LENGTH;
// no-op: will try saving again as stripe is not ready
continue;
} else {
throw error;
}
}
}
return this.product;
}),
saveStripeSettings: task(function* () {
this.set('stripeConnectError', null);
this.set('stripeConnectSuccess', null);
@ -292,8 +316,7 @@ export default Component.extend({
}
);
this.product.set('stripePrices', stripePrices);
yield timeout(3000);
const updatedProduct = yield this.product.save();
const updatedProduct = yield this.saveProduct.perform();
const monthlyPrice = this.getActivePrice(updatedProduct.stripePrices, 'month', 500, 'usd');
const yearlyPrice = this.getActivePrice(updatedProduct.stripePrices, 'year', 5000, 'usd');
this.updatePortalPlans(monthlyPrice.id, yearlyPrice.id);