Hi support team, I need your help. I keep getting this error and I am not sure how to fix this. I am using “coding” and I have a javascript. Essentially, whenever I hit “Invoke”, I get this error.
For context, I am setting up. a SaaS and will receive payments via Stripe. So when I send a test webhook in Stripe CLI using this prompt: stripe trigger checkout.session.completed
I get an error in Stripe’s sandbox dashboard with this error: HTTP status code 400 - {
“code”: 0,
“message”: “Cannot read properties of undefined (reading ‘body’)”,
“errorData”: {}
}
Am I right by thinking Backendless is already parsing JSON for me into req.body, so req.rawBody will indeed be undefined unless I explicitly switch the body-parser to “raw” in Backendless lower-level settings (which is tricky in the Console, I tried to do it but unable to find a way, is it because I am on the free version?)
Please let me know if you have any questions and any clarification.
Hi, thank you so much for the warm welcome. I provided you below the full JS code. I removed the secret keys values. Since, I tried to look for environment variables to store my keys safely, but wasn’t able to do so. So in the meantime I hard-coded them in but later will find a safer alternative once we resolve this error code. Please let me know if you need anything from me.
// services/StripeWebhookService.js
const crypto = require('crypto');
class StripeWebhookService {
/**
* Main webhook endpoint
* • POST /stripe-webhook (use JSON body parser)
* • returns { status, body }
*/
async handleStripeEvent(req) {
// 1) Hard-coded secrets (replace with secure storage later)
const stripeSecretKey = '<I will use a variable environment later>';
const webhookSecret = '<I will use a variable environment later>';
if (!stripeSecretKey || !webhookSecret) {
return { status: 500, body: 'Server config error: missing Stripe secrets.' };
}
// 2) Grab body text and signature header
// We fallback to stringified req.body since Raw mode isn't available
const payload = JSON.stringify(req.body || {});
const sigHeader = req.headers?.['stripe-signature'];
if (!sigHeader) {
return { status: 400, body: 'Missing Stripe signature header.' };
}
// 3) Verify signature (HMAC-SHA256)
// Stripe sends header in format: t=timestamp,v1=signature
const parts = sigHeader.split(',').reduce((acc, pair) => {
const [k, v] = pair.split('=');
acc[k] = v;
return acc;
}, {});
const signedPayload = `${parts.t}.${payload}`;
const expectedSig = crypto
.createHmac('sha256', webhookSecret)
.update(signedPayload)
.digest('hex');
// timing-safe compare
try {
const sigBuf = Buffer.from(parts.v1, 'hex');
const expBuf = Buffer.from(expectedSig, 'hex');
if (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf)) {
return { status: 400, body: 'Invalid Stripe signature.' };
}
} catch {
return { status: 400, body: 'Invalid Stripe signature format.' };
}
// 4) Parse the event JSON
let event;
try {
event = JSON.parse(payload);
} catch {
return { status: 400, body: 'Invalid JSON payload.' };
}
// 5) Dispatch by event type
try {
switch (event.type) {
case 'checkout.session.completed':
await this._onCheckoutCompleted(event.data.object);
break;
case 'invoice.paid':
await this._onInvoicePaid(event.data.object);
break;
case 'invoice.payment_failed':
await this._onPaymentFailed(event.data.object);
break;
case 'customer.subscription.deleted':
await this._onSubscriptionCanceled(event.data.object);
break;
default:
console.log('Unhandled Stripe event:', event.type);
}
return { status: 200, body: 'Event processed' };
} catch (err) {
console.error('Error processing Stripe event:', err);
return { status: 500, body: 'Internal Server Error' };
}
}
// ────────────────────────────────────────────────────────
// Example handler for checkout.session.completed
// ────────────────────────────────────────────────────────
async _onCheckoutCompleted(session) {
console.log('✅ checkout.session.completed', session.id);
// Here you can update your database, send emails, etc.
}
async _onInvoicePaid(invoice) {
console.log('✅ invoice.paid', invoice.id);
// Your logic here
}
async _onPaymentFailed(invoice) {
console.log('⚠️ invoice.payment_failed', invoice.id);
// Your logic here
}
async _onSubscriptionCanceled(subscription) {
console.log('ℹ️ customer.subscription.deleted', subscription.id);
// Your logic here
}
}
Backendless.ServerCode.addService(StripeWebhookService);