Files
clinera-site/pledge-now-pay-later/src/lib/stripe.ts
T

128 lines
3.2 KiB
TypeScript

import Stripe from "stripe"
let stripeClient: Stripe | null = null
export function getStripe(): Stripe | null {
if (stripeClient) return stripeClient
const key = process.env.STRIPE_SECRET_KEY
if (!key || key === "sk_test_REPLACE_ME") return null
stripeClient = new Stripe(key, {
apiVersion: "2025-01-27.acacia" as Stripe.LatestApiVersion,
typescript: true,
})
return stripeClient
}
/**
* Create a Stripe Checkout Session for a card payment.
* Returns the checkout URL to redirect the donor to.
*/
export async function createCheckoutSession(opts: {
amountPence: number
currency: string
pledgeId: string
reference: string
eventName: string
organizationName: string
donorEmail?: string
successUrl: string
cancelUrl: string
}): Promise<{ sessionId: string; checkoutUrl: string } | null> {
const stripe = getStripe()
if (!stripe) return null
try {
const session = await stripe.checkout.sessions.create({
mode: "payment",
payment_method_types: ["card"],
line_items: [
{
price_data: {
currency: opts.currency.toLowerCase(),
unit_amount: opts.amountPence,
product_data: {
name: `Donation — ${opts.eventName}`,
description: `Pledge ref: ${opts.reference} to ${opts.organizationName}`,
},
},
quantity: 1,
},
],
customer_email: opts.donorEmail || undefined,
metadata: {
pledge_id: opts.pledgeId,
reference: opts.reference,
},
success_url: opts.successUrl,
cancel_url: opts.cancelUrl,
})
return {
sessionId: session.id,
checkoutUrl: session.url!,
}
} catch (error) {
console.error("Stripe checkout session error:", error)
return null
}
}
/**
* Create a Stripe Payment Intent for embedded payment (Stripe Elements).
* Returns client secret for frontend confirmation.
*/
export async function createPaymentIntent(opts: {
amountPence: number
currency: string
pledgeId: string
reference: string
donorEmail?: string
}): Promise<{ clientSecret: string; paymentIntentId: string } | null> {
const stripe = getStripe()
if (!stripe) return null
try {
const pi = await stripe.paymentIntents.create({
amount: opts.amountPence,
currency: opts.currency.toLowerCase(),
metadata: {
pledge_id: opts.pledgeId,
reference: opts.reference,
},
receipt_email: opts.donorEmail || undefined,
automatic_payment_methods: {
enabled: true,
},
})
return {
clientSecret: pi.client_secret!,
paymentIntentId: pi.id,
}
} catch (error) {
console.error("Stripe payment intent error:", error)
return null
}
}
/**
* Verify a Stripe webhook signature.
*/
export function constructWebhookEvent(
body: string | Buffer,
signature: string
): Stripe.Event | null {
const stripe = getStripe()
const secret = process.env.STRIPE_WEBHOOK_SECRET
if (!stripe || !secret || secret === "whsec_REPLACE_ME") return null
try {
return stripe.webhooks.constructEvent(body, signature, secret)
} catch (error) {
console.error("Stripe webhook signature verification failed:", error)
return null
}
}