import * as plugins from './paypal.plugins'; import { PayPalTransaction } from './paypal.classes.transaction'; export interface IPayPalOptions { clientId: string; clientSecret: string; accountOwner: string; } export class PayPalAccount { public apiBaseUrl: string = 'https://api.paypal.com'; public options: IPayPalOptions; private apiToken: string; private apiTokenExpirationTime: number; constructor(optionsArg: IPayPalOptions) { this.options = optionsArg; } public async getTransactionsFromTo(fromTimeMillisArg: number, toTimeMillisArg: number) { let allTransactions: PayPalTransaction[] = []; // lets note the time from one month before. We need that for accurate transactions pools. const monthBeforeStartMillis = fromTimeMillisArg - plugins.smarttime.getMilliSecondsFromUnits({ days: 30 }); do { const transactions = await PayPalTransaction.getTransactionFor30days(this, fromTimeMillisArg); allTransactions = allTransactions.concat(transactions); fromTimeMillisArg = fromTimeMillisArg + plugins.smarttime.getMilliSecondsFromUnits({ days: 30 }); } while (fromTimeMillisArg < toTimeMillisArg); const invoiceIds: string[] = []; const transactionPools: PayPalTransaction[][] = []; // lets get all invoiceids allTransactions.forEach((transactionArg) => { const invoiceId = transactionArg.data.originApiObject.transaction_info.invoice_id; if (!invoiceIds.includes(invoiceId)) { invoiceIds.push(invoiceId); } }); // lets get all transactions per invoiceId invoiceIds.forEach((invoiceIdArg) => { const transactionPool: PayPalTransaction[] = []; allTransactions.forEach((transactionArg) => { if (transactionArg.data.originApiObject.transaction_info.invoice_id === invoiceIdArg) { transactionPool.push(transactionArg); } }); transactionPools.push(transactionPool); }); const previousMonthTransactions = await PayPalTransaction.getTransactionFor30days( this, monthBeforeStartMillis ); transactionPools.forEach((transactionPoolArg) => { const poolInvoiceId = transactionPoolArg[0].data.originApiObject.transaction_info.invoice_id; previousMonthTransactions.forEach((transactionArg) => { if (transactionArg.data.originApiObject.transaction_info.invoice_id === poolInvoiceId) { transactionPoolArg.push(transactionArg); } }); }); let finalTransactions: PayPalTransaction[] = []; // lets process all transactionPool transactionPools.forEach((transactionPoolArg) => { // lets detect foreign transactions let hasForeignTransactions = false; transactionPoolArg.forEach((transactionArg) => { if (transactionArg.data.currency !== 'EUR') { hasForeignTransactions = true; } }); if (hasForeignTransactions && transactionPoolArg.length < 4) { console.log( `Pool with invoiceId ${transactionPoolArg[0].data.originApiObject.transaction_info.invoice_id} is not completed yet. Omminiting ${transactionPoolArg.length} transactions as a result.` ); return; } if (hasForeignTransactions && transactionPoolArg.length === 4) { const negativeNativeTransaction = transactionPoolArg.find(transactionArg => { return transactionArg.data.amount < 0 && transactionArg.data.currency === 'EUR'; }); const negativeForeignTransaction = transactionPoolArg.find(transactionArg => { return transactionArg.data.amount < 0 && transactionArg.data.currency !== 'EUR'; }); const positiveNativeTransaction = transactionPoolArg.find(transactionArg => { return transactionArg.data.amount > 0 && transactionArg.data.currency === 'EUR'; }); const positiveForeignTransaction = transactionPoolArg.find(transactionArg => { return transactionArg.data.amount > 0 && transactionArg.data.currency !== 'EUR'; }); negativeNativeTransaction.data.name = negativeForeignTransaction.data.name; negativeNativeTransaction.data.description = negativeForeignTransaction.data.description; positiveNativeTransaction.data.name = this.options.accountOwner; positiveNativeTransaction.data.description = 'account balance transfer'; transactionPoolArg = transactionPoolArg.filter(transactionArg => transactionArg.data.currency === 'EUR'); } if (!hasForeignTransactions && transactionPoolArg.length === 2) { const positiveNativeTransaction = transactionPoolArg.find(transactionArg => { return transactionArg.data.amount > 0 && transactionArg.data.currency === 'EUR'; }); positiveNativeTransaction.data.name = this.options.accountOwner; positiveNativeTransaction.data.description = 'account balance transfer'; } // pool is ready finalTransactions = finalTransactions.concat(transactionPoolArg); }); return finalTransactions; } public async request(methodArg: 'GET' | 'POST', routeArg: string, payloadArg: any) { if (!this.apiToken || this.apiTokenExpirationTime < Date.now()) { const authHeader = `Basic ${plugins.smartstring.base64.encode( `${this.options.clientId}:${this.options.clientSecret}` )}`; const response = await plugins.smartrequest.request( `${this.apiBaseUrl}/v1/oauth2/token?grant_type=client_credentials`, { method: 'POST', headers: { Accept: 'application/json', 'Accept-Language': 'en_US', Authorization: authHeader, }, keepAlive: false, } ); this.apiToken = response.body.access_token; this.apiTokenExpirationTime = Date.now() + response.body.expires_in * 1000 - 600000; } // we have a token const response = await plugins.smartrequest.request(`${this.apiBaseUrl}${routeArg}`, { method: 'GET', headers: { Accept: 'application/json', 'Accept-Language': 'en_US', Authorization: `Bearer ${this.apiToken}`, }, }); return response.body; } }