0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

Added special handling for member import with Stripe connection

no issue

- When imported member contains stripe_customer_id data but there is no Stripe configured on the Ghost instance such import should faiil. The logic is consistent with one where import fails after not being able to find customer in linked Stripe account
- Fixed import stats to show import failures instead of "duplicate" when the validation error is of "Stripe" origin
This commit is contained in:
Nazar Gargol 2020-06-09 23:02:38 +12:00
parent 7918653da4
commit 81fc5f8eda
5 changed files with 34 additions and 12 deletions

View file

@ -190,6 +190,15 @@ const members = {
const member = model.toJSON(frame.options);
if (frame.data.members[0].stripe_customer_id) {
// let stripeEnabled = membersSubscriptionSettings && !!(membersSubscriptionSettings.paymentProcessors[0].config.secret_token) && !!(membersSubscriptionSettings.paymentProcessors[0].config.public_token);
if (!membersService.config.isStripeConnected()) {
throw new errors.ValidationError({
message: i18n.t('errors.api.members.stripeNotConnected.message'),
context: i18n.t('errors.api.members.stripeNotConnected.context'),
help: i18n.t('errors.api.members.stripeNotConnected.help')
});
}
await membersService.api.members.linkStripeCustomer(frame.data.members[0].stripe_customer_id, member);
}
@ -208,8 +217,10 @@ const members = {
}
// NOTE: failed to link Stripe customer/plan/subscription
if (model && error.message && (error.message.indexOf('customer') || error.message.indexOf('plan') || error.message.indexOf('subscription'))) {
const isStripeLinkingError = error.message && error.message.match(/customer|plan|subscription|Stripe account/g);
if (model && isStripeLinkingError) {
if (error.message.indexOf('customer') && error.code === 'resource_missing') {
error.message = `Member not imported, ${error.message}`;
error.context = i18n.t('errors.api.members.stripeCustomerNotFound.context');
error.help = i18n.t('errors.api.members.stripeCustomerNotFound.help');
}
@ -421,15 +432,16 @@ const members = {
if (inspection.isFulfilled()) {
fulfilled = fulfilled + 1;
} else {
if (inspection.reason() instanceof errors.ValidationError) {
const error = inspection.reason();
if (error instanceof errors.ValidationError && !(error.message.match(/Stripe/g))) {
duplicates = duplicates + 1;
} else {
// NOTE: if the error happens as a result of pure API call it doesn't get logged anywhere
// for this reason we have to make sure any unexpected errors are logged here
if (Array.isArray(inspection.reason())) {
logging.error(inspection.reason()[0]);
if (Array.isArray(error)) {
logging.error(error[0]);
} else {
logging.error(inspection.reason());
logging.error(error);
}
invalid = invalid + 1;

View file

@ -114,6 +114,12 @@ class MembersConfigProvider {
};
}
isStripeConnected() {
const paymentConfig = this.getStripePaymentConfig();
return (paymentConfig && paymentConfig.publicKey && paymentConfig.secretKey);
}
getStripePaymentConfig() {
const subscriptionSettings = this._settingsCache.get('members_subscription_settings');

View file

@ -369,6 +369,11 @@
"members": {
"memberNotFound": "Member not found.",
"memberAlreadyExists": "Email address is already member.",
"stripeNotConnected": {
"message": "Member not imported, Stripe account missing",
"context": "Attempting to import members with Stripe data when there is no Stripe account connected",
"help": "Connect the correct Stripe account, and re-run the import"
},
"stripeCustomerNotFound": {
"context": "The customer does not exist in currently linked Stripe account.",
"help": "Connect the correct Stripe account, and re-run the import."

View file

@ -235,10 +235,10 @@ describe('Members API', function () {
});
});
it('Can import file with duplicate stripe customer ids', function () {
it('Fails to import members with stripe_customer_id', function () {
return request
.post(localUtils.API.getApiQuery(`members/csv/`))
.attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-with-duplicate-stripe-ids.csv'))
.attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-with-stripe-ids.csv'))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -251,7 +251,7 @@ describe('Members API', function () {
should.exist(jsonResponse.meta);
should.exist(jsonResponse.meta.stats);
jsonResponse.meta.stats.imported.should.equal(1);
jsonResponse.meta.stats.imported.should.equal(0);
jsonResponse.meta.stats.duplicates.should.equal(0);
jsonResponse.meta.stats.invalid.should.equal(2);
});
@ -277,7 +277,7 @@ describe('Members API', function () {
should.exist(jsonResponse.new_today);
// 2 from fixtures and 5 imported in previous tests
jsonResponse.total.should.equal(7);
jsonResponse.total.should.equal(6);
});
});
@ -301,7 +301,7 @@ describe('Members API', function () {
should.exist(jsonResponse.new_today);
// 2 from fixtures and 5 imported in previous tests
jsonResponse.total.should.equal(7);
jsonResponse.total.should.equal(6);
});
});
@ -325,7 +325,7 @@ describe('Members API', function () {
should.exist(jsonResponse.new_today);
// 2 from fixtures and 5 imported in previous tests
jsonResponse.total.should.equal(7);
jsonResponse.total.should.equal(6);
});
});

View file

@ -1,4 +1,3 @@
email,subscribed,stripe_customer_id
member+duplicate_stripe_1@example.com,true,cus_GbbIQRd8TnFqHq
member+duplicate_stripe_2@example.com,false,cus_GbbIQRd8TnFqHq
member+unique_stripe_1@example.com,true,cus_GbbIQRd8TnFqHA
1 email subscribed stripe_customer_id
2 member+duplicate_stripe_1@example.com true cus_GbbIQRd8TnFqHq
member+duplicate_stripe_2@example.com false cus_GbbIQRd8TnFqHq
3 member+unique_stripe_1@example.com true cus_GbbIQRd8TnFqHA