UNPKG

@unchainedshop/plugins

Version:

Official plugin collection for the Unchained Engine with payment, delivery, and pricing adapters

166 lines (165 loc) 6.76 kB
import { createLogger } from '@unchainedshop/logger'; import { stripe } from "./stripe.js"; const logger = createLogger('unchained:stripe:handler'); export const WebhookEventTypes = { PAYMENT_INTENT_SUCCEEDED: 'payment_intent.succeeded', SETUP_INTENT_SUCCEEDED: 'setup_intent.succeeded', }; export async function stripeWebhookHandler(request, context) { try { const { modules, services } = context; const sig = request.headers.get('stripe-signature'); if (!sig) { logger.error('stripe-signature header was not provided for webhook'); return new Response(JSON.stringify({ success: false, message: 'stripe-signature header was not provided for webhook', name: 'MISSING_SIGNATURE', }), { status: 400, headers: { 'Content-Type': 'application/json' }, }); } if (!process.env.STRIPE_ENDPOINT_SECRET) { logger.error('env STRIPE_ENDPOINT_SECRET is required for webhook handling'); return new Response(JSON.stringify({ success: false, message: 'env STRIPE_ENDPOINT_SECRET is required for webhook handling', name: 'MISSING_ENDPOINT_SECRET', }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); } const rawBody = await request.text(); let event; try { event = stripe.webhooks.constructEvent(rawBody, sig, process.env.STRIPE_ENDPOINT_SECRET); } catch (err) { logger.error(`Error constructing event: ${err.message}`); return new Response(JSON.stringify({ success: false, message: err.message, name: err.name || 'SIGNATURE_VERIFICATION_FAILED', }), { status: 400, headers: { 'Content-Type': 'application/json' }, }); } if (!Object.values(WebhookEventTypes).includes(event.type)) { logger.info(`unhandled event type`, { type: event.type, }); return new Response(JSON.stringify({ success: false, ignored: true, name: 'UNHANDLED_EVENT_TYPE', message: `Unhandled event type: ${event.type}. Supported types: ${Object.values(WebhookEventTypes).join(', ')}`, }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } const environmentInMetadata = event.data?.object?.metadata?.environment || ''; const environmentInEnv = process.env.STRIPE_WEBHOOK_ENVIRONMENT || ''; if (environmentInMetadata !== environmentInEnv) { logger.info(`unhandled event environment`, { type: event.type, environment: environmentInMetadata, }); return new Response(JSON.stringify({ success: false, ignored: true, name: 'UNHANDLED_EVENT_ENVIRONMENT', message: `Unhandled event environment: ${environmentInMetadata}. Supported environment: ${environmentInEnv}`, }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } logger.info(`Processing event`, { type: event.type, }); if (event.type === WebhookEventTypes.PAYMENT_INTENT_SUCCEEDED) { const paymentIntent = event.data.object; const { orderPaymentId } = paymentIntent.metadata || {}; logger.info(`checkout with orderPaymentId: ${orderPaymentId}`, { type: event.type, }); await modules.orders.payments.logEvent(orderPaymentId, event); const orderPayment = await modules.orders.payments.findOrderPayment({ orderPaymentId, }); if (!orderPayment) { throw new Error(`order payment not found with orderPaymentId: ${orderPaymentId}`); } const order = await services.orders.checkoutOrder(orderPayment.orderId, { paymentContext: { paymentIntentId: paymentIntent.id, }, }); if (!order) throw new Error(`Order with id ${orderPayment.orderId} not found`); logger.info(`checkout successful`, { orderPaymentId, orderId: order._id, type: event.type, }); return new Response(JSON.stringify({ success: true, message: 'checkout successful', orderId: order._id, }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } if (event.type === WebhookEventTypes.SETUP_INTENT_SUCCEEDED) { const setupIntent = event.data.object; const { paymentProviderId, userId } = setupIntent.metadata || {}; logger.info(`registered payment credential with paymentProviderId: ${paymentProviderId}`, { type: event.type, userId, }); const paymentCredentials = await services.orders.registerPaymentCredentials(paymentProviderId, { transactionContext: { setupIntentId: setupIntent.id, }, userId, }); logger.info(`payment credentials registration successful`, { userId, paymentProviderId, paymentCredentialsId: paymentCredentials?._id, type: event.type, }); return new Response(JSON.stringify({ success: true, message: 'payment credentials registration successful', paymentCredentialsId: paymentCredentials?._id, }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } return new Response(JSON.stringify({ success: false, message: 'Unhandled event type', }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } catch (error) { logger.error(error); return new Response(JSON.stringify({ success: false, message: error.message, name: error.name, }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); } }