Skip to content

Commit c5016b1

Browse files
committed
feat(api): add payment operations to Stripe service (SSC-16)
- Implement createCheckoutSession() for Stripe Checkout - Implement createBillingPortalSession() for customer portal - Implement createPaymentIntent() for one-time payments - Implement listInvoices() for billing history - Implement getUpcomingInvoice() for preview - Support trial periods in checkout - Enable promotion codes in checkout - Include automatic payment methods This completes the payment processing capabilities. Relates to SSC-16: Payment Processing & Subscription Management
1 parent 760557f commit c5016b1

1 file changed

Lines changed: 142 additions & 0 deletions

File tree

apps/api/src/services/stripe.service.ts

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,148 @@ export class StripeService {
497497
// Don't throw error - webhook will retry
498498
}
499499
}
500+
501+
// ============================================================================
502+
// PAYMENT OPERATIONS
503+
// ============================================================================
504+
505+
/**
506+
* Create a checkout session for subscription
507+
* @param customerId - Stripe customer ID
508+
* @param priceId - Stripe price ID
509+
* @param options - Checkout session options
510+
* @returns Stripe checkout session
511+
*/
512+
async createCheckoutSession(
513+
customerId: string,
514+
priceId: string,
515+
options: {
516+
successUrl: string;
517+
cancelUrl: string;
518+
trialPeriodDays?: number;
519+
metadata?: Record<string, string>;
520+
}
521+
): Promise<Stripe.Checkout.Session> {
522+
try {
523+
const sessionData: Stripe.Checkout.SessionCreateParams = {
524+
customer: customerId,
525+
mode: 'subscription',
526+
line_items: [{ price: priceId, quantity: 1 }],
527+
success_url: options.successUrl,
528+
cancel_url: options.cancelUrl,
529+
metadata: options.metadata,
530+
allow_promotion_codes: true,
531+
billing_address_collection: 'auto',
532+
payment_method_types: ['card'],
533+
};
534+
535+
if (options.trialPeriodDays) {
536+
sessionData.subscription_data = {
537+
trial_period_days: options.trialPeriodDays,
538+
};
539+
}
540+
541+
const session = await stripe.checkout.sessions.create(sessionData);
542+
console.log(`✅ Created checkout session ${session.id}`);
543+
return session;
544+
} catch (error) {
545+
console.error('Failed to create checkout session:', error);
546+
throw new Error('Failed to create checkout session');
547+
}
548+
}
549+
550+
/**
551+
* Create billing portal session
552+
* @param customerId - Stripe customer ID
553+
* @param returnUrl - Return URL after portal session
554+
* @returns Stripe billing portal session
555+
*/
556+
async createBillingPortalSession(
557+
customerId: string,
558+
returnUrl: string
559+
): Promise<Stripe.BillingPortal.Session> {
560+
try {
561+
const session = await stripe.billingPortal.sessions.create({
562+
customer: customerId,
563+
return_url: returnUrl,
564+
});
565+
console.log(`✅ Created billing portal session for customer ${customerId}`);
566+
return session;
567+
} catch (error) {
568+
console.error('Failed to create billing portal session:', error);
569+
throw new Error('Failed to create billing portal session');
570+
}
571+
}
572+
573+
/**
574+
* Create payment intent for one-time payments
575+
* @param amount - Amount in cents
576+
* @param customerId - Stripe customer ID
577+
* @param options - Payment intent options
578+
* @returns Stripe payment intent
579+
*/
580+
async createPaymentIntent(
581+
amount: number,
582+
customerId: string,
583+
options?: {
584+
currency?: string;
585+
description?: string;
586+
metadata?: Record<string, string>;
587+
}
588+
): Promise<Stripe.PaymentIntent> {
589+
try {
590+
const paymentIntent = await stripe.paymentIntents.create({
591+
amount,
592+
currency: options?.currency || 'usd',
593+
customer: customerId,
594+
description: options?.description,
595+
metadata: options?.metadata,
596+
automatic_payment_methods: { enabled: true },
597+
});
598+
599+
console.log(`✅ Created payment intent ${paymentIntent.id}`);
600+
return paymentIntent;
601+
} catch (error) {
602+
console.error('Failed to create payment intent:', error);
603+
throw new Error('Failed to create payment intent');
604+
}
605+
}
606+
607+
/**
608+
* List invoices for a customer
609+
* @param customerId - Stripe customer ID
610+
* @param limit - Number of invoices to retrieve
611+
* @returns List of invoices
612+
*/
613+
async listInvoices(customerId: string, limit: number = 10): Promise<Stripe.Invoice[]> {
614+
try {
615+
const invoices = await stripe.invoices.list({
616+
customer: customerId,
617+
limit,
618+
});
619+
return invoices.data;
620+
} catch (error) {
621+
console.error('Failed to list invoices:', error);
622+
throw new Error('Failed to list invoices');
623+
}
624+
}
625+
626+
/**
627+
* Get upcoming invoice for a subscription
628+
* @param subscriptionId - Stripe subscription ID
629+
* @returns Upcoming invoice
630+
*/
631+
async getUpcomingInvoice(subscriptionId: string): Promise<Stripe.Invoice> {
632+
try {
633+
const invoice = await stripe.invoices.retrieveUpcoming({
634+
subscription: subscriptionId,
635+
});
636+
return invoice;
637+
} catch (error) {
638+
console.error('Failed to retrieve upcoming invoice:', error);
639+
throw new Error('Failed to retrieve upcoming invoice');
640+
}
641+
}
500642
}
501643

502644
export const stripeService = new StripeService();

0 commit comments

Comments
 (0)