import { BotContext } from '../types/botTypes'; import axios from 'axios'; import { createLogger } from '../../utils/logger'; const logger = createLogger('PayPalService'); // PayPal API Response Types interface PayPalLink { href: string; rel: string; method: string; } interface PayPalOrder { id: string; status: string; links: PayPalLink[]; } interface PayPalTokenResponse { access_token: string; token_type: string; expires_in: number; } export class PayPalService { private static instance: PayPalService; private readonly clientId: string; private readonly clientSecret: string; private readonly isProduction: boolean; private constructor(ctx: BotContext) { this.clientId = ctx.botData?.paypal_client_id || ''; this.clientSecret = ctx.botData?.paypal_client_secret || ''; this.isProduction = ctx.botData?.settings?.paypal_environment === 'production'; } public static getInstance(ctx: BotContext): PayPalService { if (!PayPalService.instance) { PayPalService.instance = new PayPalService(ctx); } return PayPalService.instance; } async createPaymentLink(userId: string, amount: number): Promise { try { const accessToken = await this.getAccessToken(); const order = await this.createPayPalOrder(amount, userId); // Find the approval URL from the order links const approvalLink = order.links.find(link => link.rel === 'approve'); if (!approvalLink) { throw new Error('No approval URL found in PayPal order'); } return approvalLink.href; } catch (error: any) { logger.error(`Error creating PayPal payment link: ${error.message}`); throw new Error(`Failed to create payment link: ${error.message}`); } } private async createPayPalOrder(amount: number, paymentId: string): Promise { try { const accessToken = await this.getAccessToken(); const baseUrl = this.isProduction ? 'https://api-m.paypal.com' : 'https://api-m.sandbox.paypal.com'; const response = await axios.post( `${baseUrl}/v2/checkout/orders`, { intent: 'CAPTURE', purchase_units: [{ amount: { currency_code: 'USD', value: amount.toString() }, custom_id: paymentId }] }, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' } } ); return response.data; } catch (error: any) { logger.error(`Error creating PayPal order: ${error.message}`); throw new Error(`Failed to create PayPal order: ${error.message}`); } } private async getAccessToken(): Promise { try { const baseUrl = this.isProduction ? 'https://api-m.paypal.com' : 'https://api-m.sandbox.paypal.com'; const response = await axios.post( `${baseUrl}/v1/oauth2/token`, 'grant_type=client_credentials', { headers: { 'Authorization': `Basic ${Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64')}`, 'Content-Type': 'application/x-www-form-urlencoded' } } ); const data = response.data as PayPalTokenResponse; return data.access_token; } catch (error: any) { logger.error(`Error getting PayPal access token: ${error.message}`); throw new Error(`Failed to get PayPal access token: ${error.message}`); } } }