mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 11:55:03 +03:00
Merged v5.22.3 into main
v5.22.3
This commit is contained in:
commit
85c5a19f33
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ghost-admin",
|
||||
"version": "5.22.2",
|
||||
"version": "5.22.3",
|
||||
"description": "Ember.js admin client for Ghost",
|
||||
"author": "Ghost Foundation",
|
||||
"homepage": "http://ghost.org",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ghost",
|
||||
"version": "5.22.2",
|
||||
"version": "5.22.3",
|
||||
"description": "The professional publishing platform",
|
||||
"author": "Ghost Foundation",
|
||||
"homepage": "https://ghost.org",
|
||||
|
@ -1,5 +1,37 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Create Stripe Checkout Session Can create a checkout session when using offers 1: [body] 1`] = `
|
||||
Object {
|
||||
"url": "https://site.com",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create Stripe Checkout Session Can create a checkout session when using offers 2: [headers] 1`] = `
|
||||
Object {
|
||||
"access-control-allow-origin": "*",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-type": "application/json",
|
||||
"vary": "Accept-Encoding",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create Stripe Checkout Session Can create a checkout session without passing a customerEmail 1: [body] 1`] = `
|
||||
Object {
|
||||
"url": "https://site.com",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create Stripe Checkout Session Can create a checkout session without passing a customerEmail 2: [headers] 1`] = `
|
||||
Object {
|
||||
"access-control-allow-origin": "*",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-type": "application/json",
|
||||
"vary": "Accept-Encoding",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create Stripe Checkout Session Does allow to create a checkout session if the customerEmail is not associated with a paid member 1: [body] 1`] = `
|
||||
Object {
|
||||
"url": "https://site.com",
|
||||
|
@ -1,3 +1,4 @@
|
||||
const querystring = require('querystring');
|
||||
const {agentProvider, mockManager, fixtureManager, matchers} = require('../../utils/e2e-framework');
|
||||
const nock = require('nock');
|
||||
const should = require('should');
|
||||
@ -55,6 +56,133 @@ describe('Create Stripe Checkout Session', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Can create a checkout session when using offers', async function () {
|
||||
const {body: {tiers}} = await adminAgent.get('/tiers/?include=monthly_price&yearly_price');
|
||||
const paidTier = tiers.find(tier => tier.type === 'paid');
|
||||
const {body: {offers: [offer]}} = await adminAgent.post('/offers/').body({
|
||||
offers: [{
|
||||
name: 'Test Offer',
|
||||
code: 'test-offer',
|
||||
cadence: 'month',
|
||||
status: 'active',
|
||||
currency: 'usd',
|
||||
type: 'percent',
|
||||
amount: 20,
|
||||
duration: 'once',
|
||||
duration_in_months: null,
|
||||
display_title: 'Test Offer',
|
||||
display_description: null,
|
||||
tier: {
|
||||
id: paidTier.id
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
nock('https://api.stripe.com')
|
||||
.persist()
|
||||
.get(/v1\/.*/)
|
||||
.reply((uri, body) => {
|
||||
const [match, resource, id] = uri.match(/\/v1\/(\w+)\/(.+)\/?/) || [null];
|
||||
if (match) {
|
||||
if (resource === 'products') {
|
||||
return [200, {
|
||||
id: id,
|
||||
active: true
|
||||
}];
|
||||
}
|
||||
if (resource === 'prices') {
|
||||
return [200, {
|
||||
id: id,
|
||||
active: true,
|
||||
currency: 'usd',
|
||||
unit_amount: 500
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
return [500];
|
||||
});
|
||||
|
||||
nock('https://api.stripe.com')
|
||||
.persist()
|
||||
.post(/v1\/.*/)
|
||||
.reply((uri, body) => {
|
||||
if (uri === '/v1/checkout/sessions') {
|
||||
return [200, {id: 'cs_123', url: 'https://site.com'}];
|
||||
}
|
||||
|
||||
if (uri === '/v1/coupons') {
|
||||
return [200, {id: 'coupon_123', url: 'https://site.com'}];
|
||||
}
|
||||
|
||||
return [500];
|
||||
});
|
||||
|
||||
await membersAgent.post('/api/create-stripe-checkout-session/')
|
||||
.body({
|
||||
customerEmail: 'free@test.com',
|
||||
offerId: offer.id
|
||||
})
|
||||
.expectStatus(200)
|
||||
.matchBodySnapshot()
|
||||
.matchHeaderSnapshot();
|
||||
});
|
||||
|
||||
it('Can create a checkout session without passing a customerEmail', async function () {
|
||||
const {body: {tiers}} = await adminAgent.get('/tiers/?include=monthly_price&yearly_price');
|
||||
|
||||
const paidTier = tiers.find(tier => tier.type === 'paid');
|
||||
|
||||
nock('https://api.stripe.com')
|
||||
.persist()
|
||||
.get(/v1\/.*/)
|
||||
.reply((uri, body) => {
|
||||
const [match, resource, id] = uri.match(/\/v1\/(\w+)\/(.+)\/?/) || [null];
|
||||
if (match) {
|
||||
if (resource === 'products') {
|
||||
return [200, {
|
||||
id: id,
|
||||
active: true
|
||||
}];
|
||||
}
|
||||
if (resource === 'prices') {
|
||||
return [200, {
|
||||
id: id,
|
||||
active: true,
|
||||
currency: 'usd',
|
||||
unit_amount: 500
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
return [500];
|
||||
});
|
||||
|
||||
nock('https://api.stripe.com')
|
||||
.persist()
|
||||
.post(/v1\/.*/)
|
||||
.reply((uri, body) => {
|
||||
if (uri === '/v1/checkout/sessions') {
|
||||
const bodyJSON = querystring.parse(body);
|
||||
// TODO: Actually work out what Stripe checks and when/how it errors
|
||||
if (bodyJSON.customerEmail) {
|
||||
return [400, {error: 'Invalid Email'}];
|
||||
}
|
||||
return [200, {id: 'cs_123', url: 'https://site.com'}];
|
||||
}
|
||||
|
||||
return [500];
|
||||
});
|
||||
|
||||
await membersAgent.post('/api/create-stripe-checkout-session/')
|
||||
.body({
|
||||
tierId: paidTier.id,
|
||||
cadence: 'month'
|
||||
})
|
||||
.expectStatus(200)
|
||||
.matchBodySnapshot()
|
||||
.matchHeaderSnapshot();
|
||||
});
|
||||
it('Does allow to create a checkout session if the customerEmail is not associated with a paid member', async function () {
|
||||
const {body: {tiers}} = await adminAgent.get('/tiers/?include=monthly_price&yearly_price');
|
||||
|
||||
|
@ -142,7 +142,7 @@ module.exports = class RouterController {
|
||||
async createCheckoutSession(req, res) {
|
||||
let ghostPriceId = req.body.priceId;
|
||||
const tierId = req.body.tierId;
|
||||
const cadence = req.body.cadence;
|
||||
let cadence = req.body.cadence;
|
||||
const identity = req.body.identity;
|
||||
const offerId = req.body.offerId;
|
||||
const metadata = req.body.metadata ?? {};
|
||||
@ -185,6 +185,7 @@ module.exports = class RouterController {
|
||||
if (offerId) {
|
||||
offer = await this._offersAPI.getOffer({id: offerId});
|
||||
tier = await this._tiersService.api.read(offer.tier.id);
|
||||
cadence = offer.cadence;
|
||||
} else {
|
||||
offer = null;
|
||||
tier = await this._tiersService.api.read(tierId);
|
||||
|
@ -67,7 +67,7 @@ class PaymentsService {
|
||||
let coupon = null;
|
||||
let trialDays = null;
|
||||
if (offer) {
|
||||
if (offer.tier.id !== tier.id) {
|
||||
if (!tier.id.equals(offer.tier.id)) {
|
||||
throw new BadRequestError({
|
||||
message: 'This Offer is not valid for the Tier'
|
||||
});
|
||||
@ -86,11 +86,13 @@ class PaymentsService {
|
||||
|
||||
const price = await this.getPriceForTierCadence(tier, cadence);
|
||||
|
||||
const email = options.email || null;
|
||||
|
||||
const session = await this.stripeAPIService.createCheckoutSession(price.id, customer, {
|
||||
metadata,
|
||||
successUrl: options.successUrl,
|
||||
cancelUrl: options.cancelUrl,
|
||||
customerEmail: options.email,
|
||||
customerEmail: customer ? email : null,
|
||||
trialDays: trialDays ?? tier.trialDays,
|
||||
coupon: coupon?.id
|
||||
});
|
||||
@ -119,8 +121,8 @@ class PaymentsService {
|
||||
|
||||
async createCustomerForMember(member) {
|
||||
const customer = await this.stripeAPIService.createCustomer({
|
||||
email: member.email,
|
||||
name: member.name
|
||||
email: member.get('email'),
|
||||
name: member.get('name')
|
||||
});
|
||||
|
||||
await this.StripeCustomerModel.add({
|
||||
|
@ -300,6 +300,11 @@ export function getAvailableProducts({site}) {
|
||||
}
|
||||
|
||||
return products.filter(product => !!product).filter((product) => {
|
||||
if (site.is_stripe_configured) {
|
||||
return true;
|
||||
}
|
||||
return product.type !== 'paid';
|
||||
}).filter((product) => {
|
||||
return !!(product.monthlyPrice && product.yearlyPrice);
|
||||
}).filter((product) => {
|
||||
return !!(Object.keys(product.monthlyPrice).length > 0 && Object.keys(product.yearlyPrice).length > 0);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {getCurrencySymbol, getFreeProduct, getMemberName, getMemberSubscription, getPriceFromSubscription, getPriceIdFromPageQuery, getSupportAddress, getUrlHistory, hasMultipleProducts, isActiveOffer, isInviteOnlySite, isPaidMember, isSameCurrency, transformApiTiersData} from './helpers';
|
||||
import {getAvailableProducts, getCurrencySymbol, getFreeProduct, getMemberName, getMemberSubscription, getPriceFromSubscription, getPriceIdFromPageQuery, getSupportAddress, getUrlHistory, hasMultipleProducts, isActiveOffer, isInviteOnlySite, isPaidMember, isSameCurrency, transformApiTiersData} from './helpers';
|
||||
import * as Fixtures from './fixtures-generator';
|
||||
import {site as FixturesSite, member as FixtureMember, offer as FixtureOffer, transformTierFixture as TransformFixtureTiers} from '../utils/test-fixtures';
|
||||
import {isComplimentaryMember} from '../utils/helpers';
|
||||
@ -294,4 +294,17 @@ describe('Helpers - ', () => {
|
||||
expect(urlHistory).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAvailableProducts', () => {
|
||||
it('Does not include paid Tiers when stripe is not configured', () => {
|
||||
const actual = getAvailableProducts({
|
||||
site: {
|
||||
...FixturesSite.multipleTiers.basic,
|
||||
is_stripe_configured: false
|
||||
}
|
||||
});
|
||||
|
||||
expect(actual.length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user