File size: 2,606 Bytes
b5f6b08
 
 
e290651
 
b5f6b08
 
 
c8a4f4b
 
 
e290651
b5f6b08
 
e290651
 
 
 
 
b5f6b08
 
 
 
 
 
e290651
b5f6b08
 
 
 
 
 
 
 
 
 
e290651
 
b5f6b08
 
 
 
 
 
 
 
 
e290651
 
b5f6b08
 
 
 
 
 
e290651
 
 
 
 
 
 
 
b5f6b08
 
 
 
 
 
d9879cf
 
b5f6b08
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import Stripe from 'stripe';

export class StripeService {
    private stripe: Stripe | null = null;
    private webhookSecret: string | null = null;
    private clientUrl: string;

    constructor() {
        const secretKey = process.env.STRIPE_SECRET_KEY;
        const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;

        this.webhookSecret = webhookSecret || null;
        this.clientUrl = process.env.VITE_CLIENT_URL || 'http://localhost:5174';

        if (secretKey) {
            this.stripe = new Stripe(secretKey, {
                apiVersion: '2025-01-27.acacia' as any,
            });
        }
    }

    /**
     * Creates a Stripe Checkout Session for a specific track and user.
     */
    async createCheckoutSession(userId: string, trackId: string, priceId: string, userPhone: string) {
        if (!this.stripe) throw new Error('[StripeService] STRIPE_SECRET_KEY is not configured');
        try {
            const session = await this.stripe.checkout.sessions.create({
                payment_method_types: ['card'],
                line_items: [
                    {
                        price: priceId,
                        quantity: 1,
                    },
                ],
                mode: 'payment',
                success_url: `${this.clientUrl}/payment/success?session_id={CHECKOUT_SESSION_ID}&track=${trackId}`,
                cancel_url: `${this.clientUrl}/student?cancel=true`,
                metadata: {
                    userId,
                    trackId,
                    userPhone
                }
            });

            return session.url;
        } catch (error) {
            console.error('[StripeService] Failed to create checkout session:', error);
            throw error;
        }
    }

    /**
     * Verifies the signature of an incoming Stripe webhook.
     */
    verifyWebhookSignature(payload: Buffer, signature: string | undefined): Stripe.Event {
        if (!this.stripe || !this.webhookSecret) {
            throw new Error('[StripeService] Stripe is not configured (missing keys)');
        }
        if (!signature) {
            throw new Error('Missing stripe-signature header');
        }

        try {
            return this.stripe.webhooks.constructEvent(
                payload,
                signature,
                this.webhookSecret
            );
        } catch (err: unknown) {
            throw new Error(`Webhook Error: ${(err instanceof Error ? (err instanceof Error ? err.message : String(err)) : String(err))}`);
        }
    }
}

export const stripeService = new StripeService();