-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
145 lines (129 loc) · 4.75 KB
/
index.js
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
const paypal = require('@paypal-rest-sdk');
const cron = require('node-cron');
const fs = require('fs').promises;
const pLimit = require('p-limit');
const mysql = require('mysql2/promise');
const { v4: uuidv4 } = require('uuid');
const retry = require('async-retry');
require('dotenv').config();
const MAX_RETRIES = process.env.MAX_RETRIES || 3;
const BACKOFF_MULTIPLIER = process.env.BACKOFF_MULTIPLIER || 2;
paypal.configure({
'mode': process.env.PAYPAL_MODE,
'client_id': process.env.PAYPAL_CLIENT_ID,
'client_secret': process.env.PAYPAL_CLIENT_SECRET
});
let pool = null;
if (process.env.MYSQL_HOST && process.env.MYSQL_USER && process.env.MYSQL_PASSWORD && process.env.MYSQL_DATABASE) {
pool = mysql.createPool({
connectionLimit: 10,
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE
});
console.log('Connected to MySQL database pool.');
} else {
console.log('MySQL credentials not found. Falling back to file logging.');
}
const log = async (message, type = 'info') => {
const timestamp = new Date().toISOString();
const logMessage = `${timestamp} - ${type.toUpperCase()}: ${message}\n`;
try {
await fs.appendFile('payout_logs.log', logMessage);
} catch (err) {
console.error('Error logging to file:', err);
}
};
const executeQuery = async (query, values) => {
try {
const [rows] = await pool.execute(query, values);
return rows;
} catch (err) {
console.error('Error executing query:', err);
throw err;
}
};
const logToDatabase = async (recipientEmail, amount, status, transactionId = null, errorMessage = null) => {
if (!pool) return;
const timestamp = new Date().toISOString();
const query = `INSERT INTO payout_history (recipient_email, amount, status, transaction_id, error_message, created_at)
VALUES (?, ?, ?, ?, ?, ?)`;
const values = [recipientEmail, amount, status, transactionId, errorMessage, timestamp];
await executeQuery(query, values);
};
const sendPayout = async (amount, recipientEmail = process.env.RECIPIENT_EMAIL) => {
const payoutConfig = {
"sender_batch_header": {
"sender_batch_id": "batch_" + uuidv4(),
"email_subject": "You have a payment",
"email_message": "You have received a payment from us!"
},
"items": [
{
"recipient_type": "EMAIL",
"amount": {
"value": amount,
"currency": process.env.PAYOUT_CURRENCY || "USD"
},
"receiver": recipientEmail,
"note": "Automatic payout every 24 hours",
"sender_item_id": "item_" + uuidv4()
}
]
};
try {
const payout = await paypal.payout.create(payoutConfig);
await logToDatabase(recipientEmail, amount, "success", payout.batch_header.payout_batch_id);
await log(`Payout successful: ${JSON.stringify(payout)}`);
} catch (error) {
await logToDatabase(recipientEmail, amount, "failed", null, error.message);
await log(`Payout failed: ${error.message}`, 'error');
throw error;
}
};
const sendPayoutWithRetry = async (amount, recipientEmail) => {
await retry(async () => {
await sendPayout(amount, recipientEmail);
}, {
retries: MAX_RETRIES,
factor: BACKOFF_MULTIPLIER,
onRetry: (error, attempt) => {
console.log(`Retry attempt #${attempt} after error: ${error.message}`);
}
});
};
const limit = pLimit(1);
const queuePayout = (amount) => {
limit(() => sendPayoutWithRetry(amount))
.then(() => console.log("Payout completed successfully"))
.catch((error) => log(`Payout failed after all retries: ${error.message}`, 'error'));
};
const getPayPalBalance = () => {
return 150.00;
}
const MINIMUM_BALANCE = 20.00;
const checkBalanceAndSendPayout = () => {
const balance = getPayPalBalance();
if (balance > MINIMUM_BALANCE) {
const amountToSend = balance - MINIMUM_BALANCE;
console.log(`Balance is sufficient, sending ${amountToSend} to recipient.`);
queuePayout(amountToSend);
} else {
console.log(`Balance too low. Current balance: $${balance}. Minimum required: $${MINIMUM_BALANCE}.`);
}
};
cron.schedule('0 0 * * *', () => {
console.log("Running payout job at midnight...");
checkBalanceAndSendPayout();
}, {
timezone: "America/New_York"
});
process.on('SIGTERM', async () => {
console.log('SIGTERM signal received. Closing gracefully...');
if (pool) {
await pool.end();
}
console.log('MySQL connection pool closed.');
process.exit(0);
});