mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-08 02:52:39 -05:00
Added E2E tests for donations API (#17694)
fixes https://github.com/TryGhost/Product/issues/3722
This commit is contained in:
parent
38c1fbea40
commit
e14df6479b
6 changed files with 230 additions and 4 deletions
|
@ -0,0 +1,13 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Create Stripe Checkout Session for Donations Can create a member checkout session for a donation 1: [body] 1`] = `
|
||||
Object {
|
||||
"url": "https://checkout.stripe.com/c/pay/fake-data",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create Stripe Checkout Session for Donations Can create an anonymous checkout session for a donation 1: [body] 1`] = `
|
||||
Object {
|
||||
"url": "https://checkout.stripe.com/c/pay/fake-data",
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,194 @@
|
|||
const {agentProvider, mockManager, fixtureManager} = require('../../utils/e2e-framework');
|
||||
const {stripeMocker} = require('../../utils/e2e-framework-mock-manager');
|
||||
const models = require('../../../core/server/models');
|
||||
const assert = require('assert/strict');
|
||||
const urlService = require('../../../core/server/services/url');
|
||||
const DomainEvents = require('@tryghost/domain-events');
|
||||
|
||||
let membersAgent, adminAgent;
|
||||
|
||||
async function getPost(id) {
|
||||
// eslint-disable-next-line dot-notation
|
||||
return await models['Post'].where('id', id).fetch({require: true});
|
||||
}
|
||||
|
||||
describe('Create Stripe Checkout Session for Donations', function () {
|
||||
before(async function () {
|
||||
const agents = await agentProvider.getAgentsForMembers();
|
||||
membersAgent = agents.membersAgent;
|
||||
adminAgent = agents.adminAgent;
|
||||
|
||||
await fixtureManager.init('posts', 'members');
|
||||
await adminAgent.loginAsOwner();
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
mockManager.mockStripe();
|
||||
mockManager.mockMail();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
mockManager.restore();
|
||||
});
|
||||
|
||||
it('Can create an anonymous checkout session for a donation', async function () {
|
||||
// Fake a visit to a post
|
||||
const post = await getPost(fixtureManager.get('posts', 0).id);
|
||||
const url = urlService.getUrlByResourceId(post.id, {absolute: false});
|
||||
|
||||
await membersAgent.post('/api/create-stripe-checkout-session/')
|
||||
.body({
|
||||
customerEmail: 'paid@test.com',
|
||||
type: 'donation',
|
||||
successUrl: 'https://example.com/?type=success',
|
||||
cancelUrl: 'https://example.com/?type=cancel',
|
||||
metadata: {
|
||||
test: 'hello',
|
||||
urlHistory: [
|
||||
{
|
||||
path: url,
|
||||
time: Date.now(),
|
||||
referrerMedium: null,
|
||||
referrerSource: 'ghost-explore',
|
||||
referrerUrl: 'https://example.com/blog/'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
.expectStatus(200)
|
||||
.matchBodySnapshot();
|
||||
|
||||
// Send a webhook of a paid invoice for this session
|
||||
await stripeMocker.sendWebhook({
|
||||
type: 'invoice.payment_succeeded',
|
||||
data: {
|
||||
object: {
|
||||
type: 'invoice',
|
||||
paid: true,
|
||||
amount_paid: 1200,
|
||||
currency: 'usd',
|
||||
customer: (stripeMocker.checkoutSessions[0].customer),
|
||||
customer_name: 'Paid Test',
|
||||
customer_email: 'exampledonation@example.com',
|
||||
metadata: {
|
||||
...(stripeMocker.checkoutSessions[0].invoice_creation?.invoice_data?.metadata ?? {})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check email received
|
||||
mockManager.assert.sentEmail({
|
||||
subject: '💰 One-time payment received: $12.00 from Paid Test',
|
||||
to: 'jbloggs@example.com'
|
||||
});
|
||||
|
||||
// Check stored in database
|
||||
const lastDonation = await models.DonationPaymentEvent.findOne({
|
||||
email: 'exampledonation@example.com'
|
||||
}, {require: true});
|
||||
assert.equal(lastDonation.get('amount'), 1200);
|
||||
assert.equal(lastDonation.get('currency'), 'usd');
|
||||
assert.equal(lastDonation.get('email'), 'exampledonation@example.com');
|
||||
assert.equal(lastDonation.get('name'), 'Paid Test');
|
||||
assert.equal(lastDonation.get('member_id'), null);
|
||||
|
||||
// Check referrer
|
||||
assert.equal(lastDonation.get('referrer_url'), 'example.com');
|
||||
assert.equal(lastDonation.get('referrer_medium'), 'Ghost Network');
|
||||
assert.equal(lastDonation.get('referrer_source'), 'Ghost Explore');
|
||||
|
||||
// Check attributed correctly
|
||||
assert.equal(lastDonation.get('attribution_id'), post.id);
|
||||
assert.equal(lastDonation.get('attribution_type'), 'post');
|
||||
assert.equal(lastDonation.get('attribution_url'), url);
|
||||
});
|
||||
|
||||
it('Can create a member checkout session for a donation', async function () {
|
||||
// Fake a visit to a post
|
||||
const post = await getPost(fixtureManager.get('posts', 0).id);
|
||||
const url = urlService.getUrlByResourceId(post.id, {absolute: false});
|
||||
|
||||
const email = 'test-member-create-donation-session@email.com';
|
||||
|
||||
const membersService = require('../../../core/server/services/members');
|
||||
const member = await membersService.api.members.create({email, name: 'Member Test'});
|
||||
const token = await membersService.api.getMemberIdentityToken(email);
|
||||
|
||||
await DomainEvents.allSettled();
|
||||
|
||||
// Check email received
|
||||
mockManager.assert.sentEmail({
|
||||
subject: '🥳 Free member signup: Member Test',
|
||||
to: 'jbloggs@example.com'
|
||||
});
|
||||
|
||||
await membersAgent.post('/api/create-stripe-checkout-session/')
|
||||
.body({
|
||||
customerEmail: email,
|
||||
identity: token,
|
||||
type: 'donation',
|
||||
successUrl: 'https://example.com/?type=success',
|
||||
cancelUrl: 'https://example.com/?type=cancel',
|
||||
metadata: {
|
||||
test: 'hello',
|
||||
urlHistory: [
|
||||
{
|
||||
path: url,
|
||||
time: Date.now(),
|
||||
referrerMedium: null,
|
||||
referrerSource: 'ghost-explore',
|
||||
referrerUrl: 'https://example.com/blog/'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
.expectStatus(200)
|
||||
.matchBodySnapshot();
|
||||
|
||||
// Send a webhook of a paid invoice for this session
|
||||
await stripeMocker.sendWebhook({
|
||||
type: 'invoice.payment_succeeded',
|
||||
data: {
|
||||
object: {
|
||||
type: 'invoice',
|
||||
paid: true,
|
||||
amount_paid: 1220,
|
||||
currency: 'eur',
|
||||
customer: (stripeMocker.checkoutSessions[0].customer),
|
||||
customer_name: 'Member Test',
|
||||
customer_email: email,
|
||||
metadata: {
|
||||
...(stripeMocker.checkoutSessions[0].invoice_creation?.invoice_data?.metadata ?? {})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check email received
|
||||
mockManager.assert.sentEmail({
|
||||
subject: '💰 One-time payment received: €12.20 from Member Test',
|
||||
to: 'jbloggs@example.com'
|
||||
});
|
||||
|
||||
// Check stored in database
|
||||
const lastDonation = await models.DonationPaymentEvent.findOne({
|
||||
email
|
||||
}, {require: true});
|
||||
assert.equal(lastDonation.get('amount'), 1220);
|
||||
assert.equal(lastDonation.get('currency'), 'eur');
|
||||
assert.equal(lastDonation.get('email'), email);
|
||||
assert.equal(lastDonation.get('name'), 'Member Test');
|
||||
assert.equal(lastDonation.get('member_id'), member.id);
|
||||
|
||||
// Check referrer
|
||||
assert.equal(lastDonation.get('referrer_url'), 'example.com');
|
||||
assert.equal(lastDonation.get('referrer_medium'), 'Ghost Network');
|
||||
assert.equal(lastDonation.get('referrer_source'), 'Ghost Explore');
|
||||
|
||||
// Check attributed correctly
|
||||
assert.equal(lastDonation.get('attribution_id'), post.id);
|
||||
assert.equal(lastDonation.get('attribution_type'), 'post');
|
||||
assert.equal(lastDonation.get('attribution_url'), url);
|
||||
});
|
||||
});
|
|
@ -80,6 +80,7 @@ const allowStripe = () => {
|
|||
|
||||
const mockStripe = () => {
|
||||
disableNetwork();
|
||||
stripeMocker.reset();
|
||||
stripeMocker.stub();
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ class StripeMocker {
|
|||
coupons = [];
|
||||
prices = [];
|
||||
products = [];
|
||||
checkoutSessions = [];
|
||||
|
||||
nockInterceptors = [];
|
||||
|
||||
|
@ -39,6 +40,7 @@ class StripeMocker {
|
|||
this.coupons = [];
|
||||
this.prices = [];
|
||||
this.products = [];
|
||||
this.checkoutSessions = [];
|
||||
|
||||
// Fix for now, because of importing order breaking some things when they are not initialized
|
||||
members = require('../../core/server/services/members');
|
||||
|
@ -227,6 +229,17 @@ class StripeMocker {
|
|||
}
|
||||
}
|
||||
|
||||
if (resource === 'checkout') {
|
||||
if (!id) {
|
||||
// Add default fields
|
||||
decoded = {
|
||||
object: 'checkout.session',
|
||||
...decoded,
|
||||
url: 'https://checkout.stripe.com/c/pay/fake-data'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'subscriptions') {
|
||||
// Convert price to price object
|
||||
if (Array.isArray(decoded.items)) {
|
||||
|
@ -381,6 +394,10 @@ class StripeMocker {
|
|||
return this.#postData(this.products, id, body, resource);
|
||||
}
|
||||
|
||||
if (resource === 'checkout' && id === 'sessions') {
|
||||
return this.#postData(this.checkoutSessions, null, body, resource);
|
||||
}
|
||||
|
||||
return [500];
|
||||
});
|
||||
|
||||
|
|
|
@ -117,13 +117,14 @@ class PaymentsService {
|
|||
* @param {Object.<string, any>} [params.metadata]
|
||||
* @param {string} params.successUrl
|
||||
* @param {string} params.cancelUrl
|
||||
* @param {boolean} [params.isAuthenticated]
|
||||
* @param {string} [params.email]
|
||||
*
|
||||
* @returns {Promise<URL>}
|
||||
*/
|
||||
async getDonationPaymentLink({member, metadata, successUrl, cancelUrl, email}) {
|
||||
async getDonationPaymentLink({member, metadata, successUrl, cancelUrl, email, isAuthenticated}) {
|
||||
let customer = null;
|
||||
if (member) {
|
||||
if (member && isAuthenticated) {
|
||||
customer = await this.getCustomerForMember(member);
|
||||
}
|
||||
|
||||
|
|
|
@ -115,9 +115,9 @@ module.exports = class WebhookController {
|
|||
// Track a one time payment event
|
||||
const amount = invoice.amount_paid;
|
||||
|
||||
const member = await this.deps.memberRepository.get({
|
||||
const member = invoice.customer ? (await this.deps.memberRepository.get({
|
||||
customer_id: invoice.customer
|
||||
});
|
||||
})) : null;
|
||||
|
||||
const data = DonationPaymentEvent.create({
|
||||
name: member?.get('name') ?? invoice.customer_name,
|
||||
|
|
Loading…
Add table
Reference in a new issue