chore: add server/payments/stripe.ts
This commit is contained in:
parent
f948547f03
commit
c9f4d63e70
1 changed files with 97 additions and 0 deletions
97
server/payments/stripe.ts
Normal file
97
server/payments/stripe.ts
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* Stripe payment integration
|
||||
*
|
||||
* Configured via STRIPE_SECRET_KEY env var.
|
||||
* STRIPE_PUBLISHABLE_KEY is exposed to the frontend via /api/config.
|
||||
*
|
||||
* Stripe is available as a secondary payment processor.
|
||||
* Authorize.net is recommended as the primary for seamless checkout.
|
||||
*/
|
||||
|
||||
import Stripe from "stripe";
|
||||
|
||||
let _stripe: Stripe | null = null;
|
||||
|
||||
function getStripe(): Stripe {
|
||||
if (!_stripe) {
|
||||
if (!process.env.STRIPE_SECRET_KEY) {
|
||||
throw new Error("STRIPE_SECRET_KEY is not configured");
|
||||
}
|
||||
_stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||
apiVersion: "2024-06-20" as any,
|
||||
});
|
||||
}
|
||||
return _stripe;
|
||||
}
|
||||
|
||||
export function isStripeConfigured(): boolean {
|
||||
return !!process.env.STRIPE_SECRET_KEY;
|
||||
}
|
||||
|
||||
export interface StripePaymentIntentResult {
|
||||
success: boolean;
|
||||
clientSecret?: string;
|
||||
paymentIntentId?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PaymentIntent for use with Stripe Elements on the frontend.
|
||||
*/
|
||||
export async function createPaymentIntent(params: {
|
||||
amountCents: number;
|
||||
currency?: string;
|
||||
email: string;
|
||||
metadata?: Record<string, string>;
|
||||
}): Promise<StripePaymentIntentResult> {
|
||||
try {
|
||||
const stripe = getStripe();
|
||||
const intent = await stripe.paymentIntents.create({
|
||||
amount: params.amountCents,
|
||||
currency: params.currency || "usd",
|
||||
receipt_email: params.email,
|
||||
metadata: params.metadata,
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
clientSecret: intent.client_secret!,
|
||||
paymentIntentId: intent.id,
|
||||
};
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a PaymentIntent by ID and check if it succeeded.
|
||||
*/
|
||||
export async function verifyPaymentIntent(
|
||||
paymentIntentId: string
|
||||
): Promise<{ success: boolean; status?: string; error?: string }> {
|
||||
try {
|
||||
const stripe = getStripe();
|
||||
const intent = await stripe.paymentIntents.retrieve(paymentIntentId);
|
||||
return { success: intent.status === "succeeded", status: intent.status };
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue a refund against a PaymentIntent.
|
||||
*/
|
||||
export async function refundPaymentIntent(params: {
|
||||
paymentIntentId: string;
|
||||
amountCents?: number;
|
||||
}): Promise<{ success: boolean; refundId?: string; error?: string }> {
|
||||
try {
|
||||
const stripe = getStripe();
|
||||
const refund = await stripe.refunds.create({
|
||||
payment_intent: params.paymentIntentId,
|
||||
...(params.amountCents && { amount: params.amountCents }),
|
||||
});
|
||||
return { success: refund.status === "succeeded", refundId: refund.id };
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue