File size: 3,279 Bytes
4a285d2
 
 
 
ef85da9
 
 
 
 
 
4a285d2
 
 
 
 
 
 
 
ef85da9
4a285d2
 
 
 
 
 
 
 
 
ef85da9
4a285d2
 
 
ef85da9
4a285d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ef85da9
4a285d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ef85da9
 
 
 
 
 
 
4a285d2
 
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
const Paystack = require('paystack-node');

export class PaystackService {
  private paystack: any;
  private initialized: boolean = false;

  private initialize() {
    if (this.initialized) {
      return;
    }

    const secretKey = process.env.PAYSTACK_SECRET_KEY;
    if (!secretKey) {
      throw new Error('PAYSTACK_SECRET_KEY not configured');
    }

    const environment = process.env.NODE_ENV === 'production' ? 'production' : 'development';
    this.paystack = new Paystack(secretKey, environment);
    this.initialized = true;
  }

  async initializeTransaction(data: {
    email: string;
    amount: number; // Amount in kobo (NGN) or smallest currency unit
    reference: string;
    metadata?: Record<string, any>;
    callback_url?: string;
  }) {
    this.initialize();
    try {
      const payload: any = {
        email: data.email,
        amount: data.amount,
        reference: data.reference,
      };

      if (data.metadata) {
        payload.metadata = JSON.stringify(data.metadata);
      }

      if (data.callback_url) {
        payload.callback_url = data.callback_url;
      }

      const response = await this.paystack.initializeTransaction(payload);

      // paystack-node returns response.body.data
      const responseData = response.body.data;

      return {
        authorization_url: responseData.authorization_url,
        access_code: responseData.access_code,
        reference: responseData.reference,
      };
    } catch (error: any) {
      console.error('Paystack initialize error:', error);
      throw new Error(`Failed to initialize transaction: ${error.message || 'Unknown error'}`);
    }
  }

  /**
   * Verify transaction by reference
   */
  async verifyTransaction(reference: string) {
    this.initialize();
    try {
      const response = await this.paystack.verifyTransaction({
        reference: reference,
      });

      // paystack-node returns response.body.data
      const responseData = response.body.data;

      // Parse metadata if it's a string (paystack-node may return it as JSON string)
      let metadata = responseData.metadata;
      if (typeof metadata === 'string') {
        try {
          metadata = JSON.parse(metadata);
        } catch (e) {
          // If parsing fails, keep as string
        }
      }

      return {
        status: responseData.status,
        reference: responseData.reference,
        amount: responseData.amount,
        currency: responseData.currency,
        customer: responseData.customer,
        metadata: metadata,
        paidAt: responseData.paid_at,
        gatewayResponse: responseData.gateway_response,
        channel: responseData.channel,
      };
    } catch (error: any) {
      console.error('Paystack verify error:', error);
      throw new Error(`Failed to verify transaction: ${error.message || 'Unknown error'}`);
    }
  }

  /**
   * Get public key for frontend
   */
  getPublicKey(): string {
    const publicKey = process.env.PAYSTACK_PUBLIC_KEY;
    if (!publicKey) {
      throw new Error('PAYSTACK_PUBLIC_KEY not configured');
    }
    return publicKey;
  }
  
  /**
   * Check if Paystack is configured
   */
  isConfigured(): boolean {
    return !!(process.env.PAYSTACK_SECRET_KEY && process.env.PAYSTACK_PUBLIC_KEY);
  }
}