Skip to content

Commit 168f3ae

Browse files
committed
Refactored
1 parent e74fdb3 commit 168f3ae

4 files changed

Lines changed: 77 additions & 85 deletions

File tree

routes/checkout.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// routes/checkout.js
21
import express from 'express';
32
import tierMap from '../config/tierMap.js';
43
import { createCheckoutSession } from '../services/stripeService.js';
@@ -18,7 +17,7 @@ router.post('/start-checkout', async (req, res) => {
1817

1918
try {
2019
const priceId = tierMap[productId][plan];
21-
const session = await createCheckoutSession(priceId, name, email);
20+
const session = await createCheckoutSession(priceId, name, email, productId, plan);
2221
res.json({ url: session.url });
2322
} catch (err) {
2423
console.error('Checkout error:', err);

routes/stripe-webhook.js

Lines changed: 57 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,40 @@ import logger from '../config/logger.js';
55

66
const router = express.Router();
77

8+
const processPaidCheckout = async (session) => {
9+
let email = session.customer_details?.email;
10+
let name = session.metadata?.name;
11+
let productId = session.metadata?.productId;
12+
let plan = session.metadata?.plan;
13+
14+
if (!email || !name) {
15+
const customer = await retrieveCustomer(session.customer);
16+
email = email || customer.email;
17+
name = name || customer.name;
18+
}
19+
20+
if (!session.subscription) {
21+
logger.warn('No subscription found in session. This is not a subscription checkout.', { sessionId: session.id });
22+
return;
23+
}
24+
25+
const subscription = await retrieveSubscription(session.subscription);
26+
27+
// CRITICAL CHECK: Ensure the plan has a price greater than zero
28+
if (subscription.items.data.length > 0 && subscription.items.data[0].price.unit_amount > 0) {
29+
await createGhostMember({
30+
email,
31+
name,
32+
stripeCustomerId: session.customer,
33+
stripeSubscriptionId: subscription.id,
34+
productId,
35+
plan
36+
});
37+
} else {
38+
logger.info('Skipping paid member creation for a zero-amount subscription.');
39+
}
40+
};
41+
842
router.post(
943
'/custom-membership-stripe-webhook',
1044
express.raw({ type: 'application/json' }),
@@ -22,65 +56,32 @@ router.post(
2256
return res.status(400).send(`Webhook Error: ${err.message}`);
2357
}
2458

25-
if (event.type === 'checkout.session.completed') {
26-
try {
27-
const session = event.data.object;
28-
logger.info('Processing checkout.session.completed event', {
29-
sessionId: session.id,
30-
customer: session.customer
31-
});
32-
33-
// Extract info from metadata & customer if available
34-
let email = session.customer_details?.email;
35-
let name = session.metadata?.name;
36-
let productId = session.metadata?.productId;
37-
let plan = session.metadata?.plan;
38-
39-
logger.debug('Extracted session metadata', {
40-
emailPresent: !!email,
41-
namePresent: !!name,
42-
productId,
43-
plan
44-
});
45-
46-
// If missing, fetch from Stripe (fallback)
47-
if (!email || !name) {
48-
logger.debug('Missing email or name, fetching customer from Stripe');
49-
const customer = await retrieveCustomer(session.customer);
50-
email = email || customer.email;
51-
name = name || customer.name;
59+
switch (event.type) {
60+
case 'checkout.session.completed':
61+
try {
62+
const session = event.data.object;
63+
if (session.payment_status === 'paid') {
64+
await processPaidCheckout(session);
65+
} else {
66+
logger.info(`Checkout session completed, but payment is not yet paid. Waiting for async_payment_succeeded. Session ID: ${session.id}`);
67+
}
68+
} catch (err) {
69+
logger.error('Error processing checkout.session.completed', { error: err.message, stack: err.stack });
5270
}
71+
break;
5372

54-
// Always fetch subscription details
55-
logger.debug('Fetching subscription details');
56-
const subscription = await retrieveSubscription(session.subscription);
57-
58-
logger.info('Creating Ghost member', {
59-
email: email?.substring(0, 3) + '...@...' + email?.split('@')[1]?.substring(0, 3),
60-
name: name?.substring(0, 2) + '...',
61-
productId,
62-
plan
63-
});
64-
65-
await createGhostMember({
66-
email,
67-
name,
68-
stripeCustomerId: session.customer,
69-
stripeSubscriptionId: subscription.id,
70-
productId,
71-
plan
72-
});
73+
case 'checkout.session.async_payment_succeeded':
74+
try {
75+
const session = event.data.object;
76+
logger.info(`Async payment succeeded for session: ${session.id}. Proceeding with member creation.`);
77+
await processPaidCheckout(session);
78+
} catch (err) {
79+
logger.error('Error processing checkout.session.async_payment_succeeded', { error: err.message, stack: err.stack });
80+
}
81+
break;
7382

74-
logger.info(`Ghost member created: ${email?.substring(0, 3)}... (${plan} plan)`);
75-
} catch (err) {
76-
logger.error('Error syncing with Ghost', {
77-
error: err.message,
78-
stack: err.stack,
79-
eventType: event.type
80-
});
81-
}
82-
} else {
83-
logger.debug('Unhandled Stripe event type', { eventType: event.type });
83+
default:
84+
logger.debug('Unhandled Stripe event type', { eventType: event.type });
8485
}
8586

8687
res.status(200).end();

services/ghostService.js

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,31 @@ const api = new GhostAdminAPI({
99
});
1010

1111
export async function createGhostMember({ email, name, stripeCustomerId, stripeSubscriptionId, productId, plan }) {
12-
  try {
13-
    logger.info(`Creating paid member: ${email.substring(0, 3)}...@...`, { productId, plan });
14-
    const result = await api.members.add({
15-
      email,
16-
      name,
17-
      stripe_customer_id: stripeCustomerId,
18-
      subscriptions: [
19-
        { stripe_subscription_id: stripeSubscriptionId }
20-
      ]
21-
    });
22-
    logger.info(`Paid member created for ${email.substring(0, 3)}...@...`, { productId, plan });
23-
    return result;
24-
  } catch (err) {
25-
    logger.error(`Failed to create paid member ${email.substring(0, 3)}...@...`, {
26-
      error: err.message,
27-
      stack: err.stack,
12+
try {
13+
logger.info(`Creating paid member: ${email.substring(0, 3)}...@...`, { productId, plan });
14+
const result = await api.members.add({
15+
email,
16+
name,
17+
stripe_customer_id: stripeCustomerId,
18+
subscriptions: [{ stripe_subscription_id: stripeSubscriptionId }]
19+
});
20+
logger.info(`Paid member created for ${email.substring(0, 3)}...@...`, { productId, plan });
21+
return result;
22+
} catch (err) {
23+
logger.error(`Failed to create paid member ${email.substring(0, 3)}...@...`, {
24+
error: err.message,
25+
stack: err.stack,
2826
productId,
2927
plan
30-
    });
31-
    throw new Error(`Ghost API error: ${err.message}`);
32-
  }
28+
});
29+
throw new Error(`Ghost API error: ${err.message}`);
30+
}
3331
}
3432

3533
export async function createFreeGhostMember({ email, name }) {
3634
try {
3735
logger.info(`Creating free member: ${email.substring(0, 3)}...@...`);
38-
const result = await api.members.add({
39-
email,
40-
name,
41-
}, { send_email: true });
36+
const result = await api.members.add({ email, name }, { send_email: true });
4237
logger.info(`Free member created for ${email.substring(0, 3)}...@...`);
4338
return result;
4439
} catch (err) {

services/stripeService.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
// services/stripeService.js
21
import Stripe from 'stripe';
32
import env from '../config/env.js';
43

54
const stripe = new Stripe(env.STRIPE_SECRET_KEY);
65

76
export async function createCheckoutSession(priceId, name, email, productId, plan) {
8-
// Create customer first with metadata
97
const customer = await stripe.customers.create({
108
name,
119
email,
@@ -15,7 +13,6 @@ export async function createCheckoutSession(priceId, name, email, productId, pla
1513
}
1614
});
1715

18-
// Create checkout session linked to that customer
1916
return stripe.checkout.sessions.create({
2017
mode: 'subscription',
2118
payment_method_types: ['card'],
@@ -41,4 +38,4 @@ export async function retrieveCustomer(customerId) {
4138

4239
export function constructStripeEvent(rawBody, signature) {
4340
return stripe.webhooks.constructEvent(rawBody, signature, env.STRIPE_WEBHOOK_SECRET);
44-
}
41+
}

0 commit comments

Comments
 (0)