Merge pull request #1642 from MPOS/payout-overhaul
[IMPROVED] Payout logics
This commit is contained in:
commit
bc0d340bf3
@ -34,169 +34,77 @@ if ($bitcoin->can_connect() !== true) {
|
|||||||
$log->logFatal(" unable to connect to RPC server, exiting");
|
$log->logFatal(" unable to connect to RPC server, exiting");
|
||||||
$monitoring->endCronjob($cron_name, 'E0006', 1, true);
|
$monitoring->endCronjob($cron_name, 'E0006', 1, true);
|
||||||
}
|
}
|
||||||
if ($setting->getValue('disable_manual_payouts') != 1) {
|
|
||||||
// Fetch outstanding payout requests
|
|
||||||
if ($aPayouts = $oPayout->getUnprocessedPayouts()) {
|
|
||||||
if (count($aPayouts) > 0) {
|
|
||||||
$log->logInfo("\tStarting Manual Payments...");
|
|
||||||
$log->logInfo("\tAccount ID\tUsername\tBalance\t\tCoin Address");
|
|
||||||
foreach ($aPayouts as $aData) {
|
|
||||||
$transaction_id = NULL;
|
|
||||||
$rpc_txid = NULL;
|
|
||||||
$aBalance = $transaction->getBalance($aData['account_id']);
|
|
||||||
$dBalance = $aBalance['confirmed'];
|
|
||||||
$aData['coin_address'] = $user->getCoinAddress($aData['account_id']);
|
|
||||||
$aData['username'] = $user->getUserName($aData['account_id']);
|
|
||||||
// Validate address against RPC
|
|
||||||
try {
|
|
||||||
$aStatus = $bitcoin->validateaddress($aData['coin_address']);
|
|
||||||
if (!$aStatus['isvalid']) {
|
|
||||||
$log->logError('User: ' . $aData['username'] . ' - Failed to verify this users coin address, skipping payout');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$log->logError('User: ' . $aData['username'] . ' - Failed to verify this users coin address, skipping payout');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($dBalance > $config['txfee_manual']) {
|
|
||||||
// To ensure we don't run this transaction again, lets mark it completed
|
|
||||||
if (!$oPayout->setProcessed($aData['id'])) {
|
|
||||||
$log->logFatal('unable to mark transactions ' . $aData['id'] . ' as processed. ERROR: ' . $oPayout->getCronError());
|
|
||||||
$monitoring->endCronjob($cron_name, 'E0010', 1, true);
|
|
||||||
}
|
|
||||||
$log->logInfo("\t" . $aData['account_id'] . "\t\t" . $aData['username'] . "\t" . $dBalance . "\t\t" . $aData['coin_address']);
|
|
||||||
if ($transaction->addTransaction($aData['account_id'], $dBalance - $config['txfee_manual'], 'Debit_MP', NULL, $aData['coin_address'], NULL)) {
|
|
||||||
// Store debit transaction ID for later update
|
|
||||||
$transaction_id = $transaction->insert_id;
|
|
||||||
if (!$transaction->addTransaction($aData['account_id'], $config['txfee_manual'], 'TXFee', NULL, $aData['coin_address']))
|
|
||||||
$log->logError('Failed to add TXFee record: ' . $transaction->getCronError());
|
|
||||||
// Mark all older transactions as archived
|
|
||||||
if (!$transaction->setArchived($aData['account_id'], $transaction->insert_id))
|
|
||||||
$log->logError('Failed to mark transactions for #' . $aData['account_id'] . ' prior to #' . $transaction->insert_id . ' as archived. ERROR: ' . $transaction->getCronError());
|
|
||||||
// Run the payouts from RPC now that the user is fully debited
|
|
||||||
try {
|
|
||||||
$rpc_txid = $bitcoin->sendtoaddress($aData['coin_address'], $dBalance - $config['txfee_manual']);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$log->logError('E0078: RPC method did not return 200 OK: Address: ' . $aData['coin_address'] . ' ERROR: ' . $e->getMessage());
|
|
||||||
// Remove this line below if RPC calls are failing but transactions are still added to it
|
|
||||||
// Don't blame MPOS if you run into issues after commenting this out!
|
|
||||||
$monitoring->endCronjob($cron_name, 'E0078', 1, true);
|
|
||||||
}
|
|
||||||
// Update our transaction and add the RPC Transaction ID
|
|
||||||
if (empty($rpc_txid) || !$transaction->setRPCTxId($transaction_id, $rpc_txid))
|
|
||||||
$log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $transaction_id . ': ' . $transaction->getCronError());
|
|
||||||
// Notify user via mail
|
|
||||||
$aMailData['email'] = $user->getUserEmail($user->getUserName($aData['account_id']));
|
|
||||||
$aMailData['subject'] = 'Manual Payout Completed';
|
|
||||||
$aMailData['amount'] = $dBalance - $config['txfee_manual'];
|
|
||||||
$aMailData['payout_id'] = $aData['id'];
|
|
||||||
if (!$notification->sendNotification($aData['account_id'], 'manual_payout', $aMailData))
|
|
||||||
$log->logError('Failed to send notification email to users address: ' . $aMailData['email'] . 'ERROR: ' . $notification->getCronError());
|
|
||||||
// Recheck the users balance to make sure it is now 0
|
|
||||||
if (!$aBalance = $transaction->getBalance($aData['account_id'])) {
|
|
||||||
$log->logFatal('Failed to fetch balance for account ' . $aData['account_id'] . '. ERROR: ' . $transaction->getCronError());
|
|
||||||
$monitoring->endCronjob($cron_name, 'E0065', 1, true);
|
|
||||||
}
|
|
||||||
if ($aBalance['confirmed'] > 0) {
|
|
||||||
$log->logFatal('User has a remaining balance of ' . $aBalance['confirmed'] . ' after a successful payout!');
|
|
||||||
$monitoring->endCronjob($cron_name, 'E0065', 1, true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$log->logFatal('Failed to add new Debit_MP transaction in database for user ' . $user->getUserName($aData['account_id']) . ' ERROR: ' . $transaction->getCronError());
|
|
||||||
$monitoring->endCronjob($cron_name, 'E0064', 1, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
// Fetch our manual payouts, process them
|
||||||
|
if ($setting->getValue('disable_manual_payouts') != 1 && $aManualPayouts = $transaction->getMPQueue()) {
|
||||||
|
$log->logInfo(' found ' . count($aManualPayouts) . ' queued manual payouts');
|
||||||
|
$mask = ' | %-10.10s | %-25.25s | %-20.20s | %-40.40s | %-20.20s |';
|
||||||
|
$log->logInfo(sprintf($mask, 'UserID', 'Username', 'Balance', 'Address', 'Payout ID'));
|
||||||
|
foreach ($aManualPayouts as $aUserData) {
|
||||||
|
$transaction_id = NULL;
|
||||||
|
$rpc_txid = NULL;
|
||||||
|
$log->logInfo(sprintf($mask, $aUserData['id'], $aUserData['username'], $aUserData['confirmed'], $aUserData['coin_address'], $aUserData['payout_id']));
|
||||||
|
if (!$oPayout->setProcessed($aUserData['payout_id'])) {
|
||||||
|
$log->logFatal(' unable to mark transactions ' . $aData['id'] . ' as processed. ERROR: ' . $oPayout->getCronError());
|
||||||
|
$monitoring->endCronjob($cron_name, 'E0010', 1, true);
|
||||||
}
|
}
|
||||||
} else if (empty($aPayouts)) {
|
if ($bitcoin->validateaddress($aUserData['coin_address'])) {
|
||||||
$log->logInfo("\tStopping Payments. No Payout Requests Found.");
|
if (!$transaction_id = $transaction->createDebitAPRecord($aUserData['id'], $aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual'])) {
|
||||||
} else {
|
$log->logFatal(' failed to fullt debit user ' . $aUserData['username'] . ': ' . $transaction->getCronError());
|
||||||
$log->logFatal("\tFailed Processing Manual Payment Queue...Aborting...");
|
$monitoring->endCronjob($cron_name, 'E0064', 1, true);
|
||||||
$monitoring->endCronjob($cron_name, 'E0050', 1, true);
|
} else {
|
||||||
}
|
// Run the payouts from RPC now that the user is fully debited
|
||||||
if (count($aPayouts > 0)) $log->logDebug(" found " . count($aPayouts) . " queued manual payout requests");
|
|
||||||
} else {
|
|
||||||
$log->logDebug("Manual payouts are disabled via admin panel");
|
|
||||||
}
|
|
||||||
if ($setting->getValue('disable_auto_payouts') != 1) {
|
|
||||||
// Fetch all users balances
|
|
||||||
if ($users = $transaction->getAPQueue()) {
|
|
||||||
if (!empty($users)) {
|
|
||||||
if (count($users) > 0) $log->logDebug(" found " . count($users) . " queued payout(s)");
|
|
||||||
// Go through users and run transactions
|
|
||||||
$log->logInfo("Starting Payments...");
|
|
||||||
$log->logInfo("\tUserID\tUsername\tBalance\tThreshold\tAddress");
|
|
||||||
foreach ($users as $aUserData) {
|
|
||||||
$transaction_id = NULL;
|
|
||||||
$rpc_txid = NULL;
|
|
||||||
$dBalance = $aUserData['confirmed'];
|
|
||||||
// Validate address against RPC
|
|
||||||
try {
|
try {
|
||||||
$aStatus = $bitcoin->validateaddress($aUserData['coin_address']);
|
$rpc_txid = $bitcoin->sendtoaddress($aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual']);
|
||||||
if (!$aStatus['isvalid']) {
|
|
||||||
$log->logError('User: ' . $aUserData['username'] . ' - Failed to verify this users coin address, skipping payout');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$log->logError('User: ' . $aUserData['username'] . ' - Failed to verify this users coin address, skipping payout');
|
$log->logError('E0078: RPC method did not return 200 OK: Address: ' . $aUserData['coin_address'] . ' ERROR: ' . $e->getMessage());
|
||||||
continue;
|
// Remove this line below if RPC calls are failing but transactions are still added to it
|
||||||
}
|
// Don't blame MPOS if you run into issues after commenting this out!
|
||||||
$log->logInfo("\t" . $aUserData['id'] . "\t" . $aUserData['username'] . "\t" . $dBalance . "\t" . $aUserData['ap_threshold'] . "\t\t" . $aUserData['coin_address']);
|
$monitoring->endCronjob($cron_name, 'E0078', 1, true);
|
||||||
// Only run if balance meets threshold and can pay the potential transaction fee
|
|
||||||
if ($dBalance > $aUserData['ap_threshold'] && $dBalance > $config['txfee_auto']) {
|
|
||||||
// Create transaction record
|
|
||||||
if ($transaction->addTransaction($aUserData['id'], $dBalance - $config['txfee_auto'], 'Debit_AP', NULL, $aUserData['coin_address'], NULL)) {
|
|
||||||
// Store debit ID for later update
|
|
||||||
$transaction_id = $transaction->insert_id;
|
|
||||||
if (!$transaction->addTransaction($aUserData['id'], $config['txfee_auto'], 'TXFee', NULL, $aUserData['coin_address']))
|
|
||||||
$log->logError('Failed to add TXFee record: ' . $transaction->getCronError());
|
|
||||||
// Mark all older transactions as archived
|
|
||||||
if (!$transaction->setArchived($aUserData['id'], $transaction->insert_id))
|
|
||||||
$log->logError('Failed to mark transactions for user #' . $aUserData['id'] . ' prior to #' . $transaction->insert_id . ' as archived. ERROR: ' . $transaction->getCronError());
|
|
||||||
// Run the payouts from RPC now that the user is fully debited
|
|
||||||
try {
|
|
||||||
$rpc_txid = $bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance - $config['txfee_auto']);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$log->logError('E0078: RPC method did not return 200 OK: Address: ' . $aUserData['coin_address'] . ' ERROR: ' . $e->getMessage());
|
|
||||||
// Remove this line below if RPC calls are failing but transactions are still added to it
|
|
||||||
// Don't blame MPOS if you run into issues after commenting this out!
|
|
||||||
$monitoring->endCronjob($cron_name, 'E0078', 1, true);
|
|
||||||
}
|
|
||||||
// Update our transaction and add the RPC Transaction ID
|
|
||||||
if (empty($rpc_txid) || !$transaction->setRPCTxId($transaction_id, $rpc_txid))
|
|
||||||
$log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $transaction_id . ': ' . $transaction->getCronError());
|
|
||||||
// Notify user via mail
|
|
||||||
$aMailData['email'] = $user->getUserEmail($user->getUserName($aUserData['id']));
|
|
||||||
$aMailData['subject'] = 'Auto Payout Completed';
|
|
||||||
$aMailData['amount'] = $dBalance - $config['txfee_auto'];
|
|
||||||
if (!$notification->sendNotification($aUserData['id'], 'auto_payout', $aMailData))
|
|
||||||
$log->logError('Failed to send notification email to users address: ' . $aMailData['email'] . ' ERROR: ' . $notification->getCronError());
|
|
||||||
// Recheck the users balance to make sure it is now 0
|
|
||||||
$aBalance = $transaction->getBalance($aUserData['id']);
|
|
||||||
if ($aBalance['confirmed'] > 0) {
|
|
||||||
$log->logFatal('User has a remaining balance of ' . $aBalance['confirmed'] . ' after a successful payout!');
|
|
||||||
$monitoring->endCronjob($cron_name, 'E0065', 1, true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$log->logFatal('Failed to add new Debit_AP transaction in database for user ' . $user->getUserName($aUserData['id']) . ' ERROR: ' . $transaction->getCronError());
|
|
||||||
$monitoring->endCronjob($cron_name, 'E0064', 1, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// Update our transaction and add the RPC Transaction ID
|
||||||
|
if (empty($rpc_txid) || !$transaction->setRPCTxId($transaction_id, $rpc_txid))
|
||||||
|
$log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $transaction_id . ': ' . $transaction->getCronError());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$log->logInfo(' failed to validate address for user: ' . $aUserData['username']);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch our auto payouts, process them
|
||||||
|
if ($setting->getValue('disable_auto_payouts') != 1 && $aAutoPayouts = $transaction->getAPQueue()) {
|
||||||
|
$log->logInfo(' found ' . count($aAutoPayouts) . ' queued auto payouts');
|
||||||
|
$mask = ' | %-10.10s | %-25.25s | %-20.20s | %-40.40s | %-20.20s |';
|
||||||
|
$log->logInfo(sprintf($mask, 'UserID', 'Username', 'Balance', 'Address', 'Threshold'));
|
||||||
|
foreach ($aAutoPayouts as $aUserData) {
|
||||||
|
$transaction_id = NULL;
|
||||||
|
$rpc_txid = NULL;
|
||||||
|
$log->logInfo(sprintf($mask, $aUserData['id'], $aUserData['username'], $aUserData['confirmed'], $aUserData['coin_address'], $aUserData['ap_threshold']));
|
||||||
|
if ($bitcoin->validateaddress($aUserData['coin_address'])) {
|
||||||
|
if (!$transaction_id = $transaction->createDebitAPRecord($aUserData['id'], $aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual'])) {
|
||||||
|
$log->logFatal(' failed to fully debit user ' . $aUserData['username'] . ': ' . $transaction->getCronError());
|
||||||
|
$monitoring->endCronjob($cron_name, 'E0064', 1, true);
|
||||||
|
} else {
|
||||||
|
// Run the payouts from RPC now that the user is fully debited
|
||||||
|
try {
|
||||||
|
$rpc_txid = $bitcoin->sendtoaddress($aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual']);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$log->logError('E0078: RPC method did not return 200 OK: Address: ' . $aUserData['coin_address'] . ' ERROR: ' . $e->getMessage());
|
||||||
|
// Remove this line below if RPC calls are failing but transactions are still added to it
|
||||||
|
// Don't blame MPOS if you run into issues after commenting this out!
|
||||||
|
$monitoring->endCronjob($cron_name, 'E0078', 1, true);
|
||||||
|
}
|
||||||
|
// Update our transaction and add the RPC Transaction ID
|
||||||
|
if (empty($rpc_txid) || !$transaction->setRPCTxId($transaction_id, $rpc_txid))
|
||||||
|
$log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $transaction_id . ': ' . $transaction->getCronError());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$log->logInfo(' failed to validate address for user: ' . $aUserData['username']);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
} else if(empty($users)) {
|
|
||||||
$log->logInfo("\tSkipping payments. No Auto Payments Eligible.");
|
|
||||||
$log->logDebug("Users have not configured their AP > 0");
|
|
||||||
} else{
|
|
||||||
$log->logFatal("\tFailed Processing Auto Payment Payment Queue. ERROR: " . $transaction->getCronError());
|
|
||||||
$monitoring->endCronjob($cron_name, 'E0050', 1, true);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$log->logDebug("Auto payouts disabled via admin panel");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$log->logInfo("Completed Payouts");
|
|
||||||
// Cron cleanup and monitoring
|
|
||||||
require_once('cron_end.inc.php');
|
require_once('cron_end.inc.php');
|
||||||
?>
|
|
||||||
|
|||||||
@ -55,6 +55,12 @@ class Base {
|
|||||||
public function setBlock($block) {
|
public function setBlock($block) {
|
||||||
$this->block = $block;
|
$this->block = $block;
|
||||||
}
|
}
|
||||||
|
public function setPayout($payout) {
|
||||||
|
$this->payout = $payout;
|
||||||
|
}
|
||||||
|
public function setNotification($notification) {
|
||||||
|
$this->notification = $notification;
|
||||||
|
}
|
||||||
public function setTransaction($transaction) {
|
public function setTransaction($transaction) {
|
||||||
$this->transaction = $transaction;
|
$this->transaction = $transaction;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -296,4 +296,16 @@ class BitcoinClient extends jsonRPCClient {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function validateaddress($coin_address) {
|
||||||
|
try {
|
||||||
|
$aStatus = parent::validateaddress($coin_address);
|
||||||
|
if (!$aStatus['isvalid']) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,18 +16,6 @@ class Payout Extends Base {
|
|||||||
return $this->sqlError('E0048');
|
return $this->sqlError('E0048');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all new, unprocessed payout requests
|
|
||||||
* @param none
|
|
||||||
* @return data Associative array with DB Fields
|
|
||||||
**/
|
|
||||||
public function getUnprocessedPayouts() {
|
|
||||||
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE completed = 0");
|
|
||||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
|
||||||
return $result->fetch_all(MYSQLI_ASSOC);
|
|
||||||
return $this->sqlError('E0050');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a new payout request
|
* Insert a new payout request
|
||||||
* @param account_id int Account ID
|
* @param account_id int Account ID
|
||||||
|
|||||||
@ -312,21 +312,110 @@ class Transaction extends Base {
|
|||||||
ON t.account_id = a.id
|
ON t.account_id = a.id
|
||||||
WHERE t.archived = 0 AND a.ap_threshold > 0 AND a.coin_address IS NOT NULL AND a.coin_address != ''
|
WHERE t.archived = 0 AND a.ap_threshold > 0 AND a.coin_address IS NOT NULL AND a.coin_address != ''
|
||||||
GROUP BY t.account_id
|
GROUP BY t.account_id
|
||||||
HAVING confirmed > a.ap_threshold
|
HAVING confirmed > a.ap_threshold AND confirmed > " . $this->config['txfee_auto']);
|
||||||
");
|
|
||||||
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||||
return $result->fetch_all(MYSQLI_ASSOC);
|
return $result->fetch_all(MYSQLI_ASSOC);
|
||||||
return $this->sqlError();
|
return $this->sqlError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debit a user account
|
||||||
|
* @param account_id int Account ID
|
||||||
|
* @param coin_address string Coin Address
|
||||||
|
* @param amount float Balance to record
|
||||||
|
* @return int Debit transaction ID or false
|
||||||
|
**/
|
||||||
|
public function createDebitMPRecord($account_id, $coin_address, $amount) {
|
||||||
|
return $this->createDebitRecord($account_id, $coin_address, $amount, 'Debit_MP');
|
||||||
|
}
|
||||||
|
public function createDebitAPRecord($account_id, $coin_address, $amount) {
|
||||||
|
return $this->createDebitRecord($account_id, $coin_address, $amount, 'Debit_AP');
|
||||||
|
}
|
||||||
|
private function createDebitRecord($account_id, $coin_address, $amount, $type) {
|
||||||
|
$type == 'Debit_MP' ? $txfee = $this->config['txfee_manual'] : $txfee = $this->config['txfee_auto'];
|
||||||
|
// Add Debit record
|
||||||
|
if (!$this->addTransaction($account_id, $amount, $type, NULL, $coin_address, NULL)) {
|
||||||
|
$this->setErrorMessage('Failed to create ' . $type . ' transaction record in database');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Fetch the inserted record ID so we can return this at the end
|
||||||
|
$transaction_id = $this->insert_id;
|
||||||
|
// Add TXFee record
|
||||||
|
if (!$this->addTransaction($account_id, $txfee, 'TXFee', NULL, $coin_address)) {
|
||||||
|
$this->setErrorMessage('Failed to create TXFee transaction record in database: ' . $this->getError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Mark transactions archived
|
||||||
|
if (!$this->setArchived($account_id, $this->insert_id)) {
|
||||||
|
$this->setErrorMessage('Failed to mark transactions <= #' . $this->insert_id . ' as archived. ERROR: ' . $this->getError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Recheck the users balance to make sure it is now 0
|
||||||
|
if (!$aBalance = $this->getBalance($account_id)) {
|
||||||
|
$this->setErrorMessage('Failed to fetch balance for account ' . $account_id . '. ERROR: ' . $this->getCronError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($aBalance['confirmed'] > 0) {
|
||||||
|
$this->setErrorMessage('User has a remaining balance of ' . $aBalance['confirmed'] . ' after a successful payout!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Notify user via mail
|
||||||
|
$aMailData['email'] = $this->user->getUserEmailById($account_id);
|
||||||
|
$aMailData['subject'] = $type . ' Completed';
|
||||||
|
$aMailData['amount'] = $amount - $txfee;
|
||||||
|
if (!$this->notification->sendNotification($account_id, 'payout', $aMailData)) {
|
||||||
|
$this->setErrorMessage('Failed to send notification email to users address: ' . $aMailData['email'] . 'ERROR: ' . $this->notification->getCronError());
|
||||||
|
}
|
||||||
|
return $transaction_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all new, unprocessed manual payout requests
|
||||||
|
* @param none
|
||||||
|
* @return data Associative array with DB Fields
|
||||||
|
**/
|
||||||
|
public function getMPQueue() {
|
||||||
|
$stmt = $this->mysqli->prepare("
|
||||||
|
SELECT
|
||||||
|
a.id,
|
||||||
|
a.username,
|
||||||
|
a.ap_threshold,
|
||||||
|
a.coin_address,
|
||||||
|
p.id AS payout_id,
|
||||||
|
IFNULL(
|
||||||
|
ROUND(
|
||||||
|
(
|
||||||
|
SUM( IF( ( t.type IN ('Credit','Bonus') AND b.confirmations >= " . $this->config['confirmations'] . ") OR t.type = 'Credit_PPS', t.amount, 0 ) ) -
|
||||||
|
SUM( IF( t.type IN ('Debit_MP', 'Debit_AP'), t.amount, 0 ) ) -
|
||||||
|
SUM( IF( ( t.type IN ('Donation','Fee') AND b.confirmations >= " . $this->config['confirmations'] . ") OR ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ), t.amount, 0 ) )
|
||||||
|
), 8
|
||||||
|
), 0
|
||||||
|
) AS confirmed
|
||||||
|
FROM " . $this->payout->getTableName() . " AS p
|
||||||
|
JOIN " . $this->user->getTableName() . " AS a
|
||||||
|
ON p.account_id = a.id
|
||||||
|
JOIN " . $this->getTableName() . " AS t
|
||||||
|
ON t.account_id = p.account_id
|
||||||
|
JOIN " . $this->block->getTableName() . " AS b
|
||||||
|
ON t.block_id = b.id
|
||||||
|
WHERE p.completed = 0 AND t.archived = 0 AND a.coin_address IS NOT NULL AND a.coin_address != ''
|
||||||
|
GROUP BY t.account_id
|
||||||
|
HAVING confirmed > " . $this->config['txfee_manual']);
|
||||||
|
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
|
||||||
|
return $result->fetch_all(MYSQLI_ASSOC);
|
||||||
|
return $this->sqlError('E0050');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$transaction = new Transaction();
|
$transaction = new Transaction();
|
||||||
$transaction->setMemcache($memcache);
|
$transaction->setMemcache($memcache);
|
||||||
|
$transaction->setNotification($notification);
|
||||||
$transaction->setDebug($debug);
|
$transaction->setDebug($debug);
|
||||||
$transaction->setMysql($mysqli);
|
$transaction->setMysql($mysqli);
|
||||||
$transaction->setConfig($config);
|
$transaction->setConfig($config);
|
||||||
$transaction->setBlock($block);
|
$transaction->setBlock($block);
|
||||||
$transaction->setUser($user);
|
$transaction->setUser($user);
|
||||||
|
$transaction->setPayout($oPayout);
|
||||||
$transaction->setErrorCodes($aErrorCodes);
|
$transaction->setErrorCodes($aErrorCodes);
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@ -25,8 +25,8 @@ class User extends Base {
|
|||||||
public function getUserEmail($username, $lower=false) {
|
public function getUserEmail($username, $lower=false) {
|
||||||
return $this->getSingle($username, 'email', 'username', 's', $lower);
|
return $this->getSingle($username, 'email', 'username', 's', $lower);
|
||||||
}
|
}
|
||||||
public function getUserNotifyEmail($username, $lower=false) {
|
public function getUserEmailById($id) {
|
||||||
return $this->getSingle($username, 'notify_email', 'username', 's', $lower);
|
return $this->getSingle($id, 'email', 'id', 'i');
|
||||||
}
|
}
|
||||||
public function getUserNoFee($id) {
|
public function getUserNoFee($id) {
|
||||||
return $this->getSingle($id, 'no_fees', 'id');
|
return $this->getSingle($id, 'no_fees', 'id');
|
||||||
@ -292,6 +292,16 @@ class User extends Base {
|
|||||||
return $this->getSingle($userID, 'coin_address', 'id');
|
return $this->getSingle($userID, 'coin_address', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a coin address exists already
|
||||||
|
* @param address string Coin Address
|
||||||
|
* @return bool True of false
|
||||||
|
**/
|
||||||
|
public function existsCoinAddress($address) {
|
||||||
|
$this->debug->append("STA " . __METHOD__, 4);
|
||||||
|
return $this->getSingle($address, 'coin_address', 'coin_address') === $address;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch users donation value
|
* Fetch users donation value
|
||||||
* @param userID int UserID
|
* @param userID int UserID
|
||||||
@ -437,21 +447,21 @@ class User extends Base {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!empty($address)) {
|
if (!empty($address)) {
|
||||||
|
if ($this->existsCoinAddress($address)) {
|
||||||
|
$this->setErrorMessage('Address is already in use');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if ($this->bitcoin->can_connect() === true) {
|
if ($this->bitcoin->can_connect() === true) {
|
||||||
try {
|
if (!$this->bitcoin->validateaddress($address)) {
|
||||||
$aStatus = $this->bitcoin->validateaddress($address);
|
$this->setErrorMessage('Invalid coin address');
|
||||||
if (!$aStatus['isvalid']) {
|
|
||||||
$this->setErrorMessage('Invalid coin address');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->setErrorMessage('Unable to verify coin address');
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->setErrorMessage('Unable to connect to RPC server for coin address validation');
|
$this->setErrorMessage('Unable to connect to RPC server for coin address validation');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$address = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number sanitizer, just in case we fall through above
|
// Number sanitizer, just in case we fall through above
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
|
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
|
||||||
|
|
||||||
define('MPOS_VERSION', '0.0.3');
|
define('MPOS_VERSION', '0.0.3');
|
||||||
define('DB_VERSION', '0.0.4');
|
define('DB_VERSION', '0.0.5');
|
||||||
define('CONFIG_VERSION', '0.0.7');
|
define('CONFIG_VERSION', '0.0.7');
|
||||||
|
|
||||||
// Fetch installed database version
|
// Fetch installed database version
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
<html>
|
|
||||||
<body>
|
|
||||||
<p>An manual payout request completed.</p>
|
|
||||||
<p>Amount: {nocache}{$DATA.amount}{/nocache}</p>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<p>An automated payout completed.</p>
|
<p>You account has been debited and the coins have been sent to your wallet.</p>
|
||||||
<p>Amount: {nocache}{$DATA.amount}{/nocache}</p>
|
<p>Amount: {nocache}{$DATA.amount}{/nocache}</p>
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
@ -33,6 +33,9 @@ require_once('shared.inc.php');
|
|||||||
// Fetch all users
|
// Fetch all users
|
||||||
$users = $user->getAllAssoc();
|
$users = $user->getAllAssoc();
|
||||||
|
|
||||||
|
// Duplicate address check
|
||||||
|
$aAllAddresses = array();
|
||||||
|
|
||||||
// Table mask
|
// Table mask
|
||||||
$mask = "| %-35.35s | %-35.35s | %-40.40s | %-7.7s |\n";
|
$mask = "| %-35.35s | %-35.35s | %-40.40s | %-7.7s |\n";
|
||||||
echo 'Validating all coin addresses. This may take some time.' . PHP_EOL . PHP_EOL;
|
echo 'Validating all coin addresses. This may take some time.' . PHP_EOL . PHP_EOL;
|
||||||
@ -44,12 +47,17 @@ foreach ($users as $aData) {
|
|||||||
} else if ($aData['is_locked'] == 1) {
|
} else if ($aData['is_locked'] == 1) {
|
||||||
$status = 'LOCKED';
|
$status = 'LOCKED';
|
||||||
} else {
|
} else {
|
||||||
$ret = $bitcoin->validateaddress($aData['coin_address']);
|
if ($bitcoin->validateaddress($aData['coin_address'])) {
|
||||||
if ($ret['isvalid']) {
|
|
||||||
$status = 'VALID';
|
$status = 'VALID';
|
||||||
} else {
|
} else {
|
||||||
$status = 'INVALID';
|
$status = 'INVALID';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Duplicate check
|
||||||
|
if (in_array($aData['coin_address'], $aAllAddresses)) {
|
||||||
|
$status = 'DUPE';
|
||||||
|
} else if (!empty($aData['coin_address'])) {
|
||||||
|
$aAllAddresses[] = $aData['coin_address'];
|
||||||
|
}
|
||||||
printf($mask, $aData['username'], $aData['email'], $aData['coin_address'], $status);
|
printf($mask, $aData['username'], $aData['email'], $aData['coin_address'], $status);
|
||||||
}
|
}
|
||||||
|
|||||||
2
sql/015_accounts_update.sql
Normal file
2
sql/015_accounts_update.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `accounts` ADD UNIQUE INDEX ( `coin_address` ) ;
|
||||||
|
INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.5') ON DUPLICATE KEY UPDATE `value` = '0.0.5';
|
||||||
Loading…
Reference in New Issue
Block a user