Adding manual payout cron
This will avoid double payouts via the website. Payouts will be requested by users and processed by a cron. If, for whatever reason, users do add two requests (it is checked if a payout exists) they would only have one successful payout until their account balance is back up to a save value to trigger the payout. This should fix any issues with manual payouts being exploited through the website. Will require some testing by others to ensure things work as expected.
This commit is contained in:
parent
44c31fe630
commit
ee5e2c46c6
82
cronjobs/manual_payout.php
Executable file
82
cronjobs/manual_payout.php
Executable file
@ -0,0 +1,82 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
/*
|
||||
|
||||
Copyright:: 2013, Sebastian Grewe
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// Include all settings and classes
|
||||
require_once('shared.inc.php');
|
||||
|
||||
if ($bitcoin->can_connect() !== true) {
|
||||
$log->logFatal(" unable to connect to RPC server, exiting");
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// var_dump($oPayout->createPayout(1.12, 1));
|
||||
$aPayouts = $oPayout->getUnprocessedPayouts();
|
||||
|
||||
if (count($aPayouts) > 0) {
|
||||
$log->logInfo("\tAccount ID\tUsername\tBalance\t\tCoin Address");
|
||||
foreach ($aPayouts as $aData) {
|
||||
$aBalance = $transaction->getBalance($aData['account_id']);
|
||||
$dBalance = $aBalance['confirmed'];
|
||||
$aData['coin_address'] = $user->getCoinAddress($aData['account_id']);
|
||||
$aData['username'] = $user->getUserName($aData['account_id']);
|
||||
if ($dBalance > $config['txfee']) {
|
||||
$log->logInfo("\t" . $aData['account_id'] . "\t\t" . $aData['username'] . "\t" . $dBalance . "\t\t" . $aData['coin_address']);
|
||||
try {
|
||||
$bitcoin->validateaddress($aData['coin_address']);
|
||||
} catch (BitcoinClientException $e) {
|
||||
$log->logError('Failed to verify this users coin address, skipping payout');
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$bitcoin->sendtoaddress($aData['coin_address'], $dBalance);
|
||||
} catch (BitcoinClientException $e) {
|
||||
$log->logError('Failed to send requested balance to coin address, please check payout process');
|
||||
continue;
|
||||
}
|
||||
// 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.');
|
||||
$monitoring->setStatus($cron_name . "_active", "yesno", 0);
|
||||
$monitoring->setStatus($cron_name . "_message", "message", "Unable set payout as processed");
|
||||
$monitoring->setStatus($cron_name . "_status", "okerror", 1);
|
||||
exit(1);
|
||||
}
|
||||
if ($transaction->addTransaction($aData['account_id'], $dBalance - $config['txfee'], 'Debit_MP', NULL, $aData['coin_address']) && $transaction->addTransaction($aData['account_id'], $config['txfee'], 'TXFee', NULL, $aData['coin_address'])) {
|
||||
// Notify user via mail
|
||||
$aMailData['email'] = $user->getUserEmail($user->getUserName($aData['account_id']));
|
||||
$aMailData['subject'] = 'Manual Payout Completed';
|
||||
$aMailData['amount'] = $dBalance;
|
||||
$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']);
|
||||
} else {
|
||||
$log->logError('Failed to add new Debit_MP transaction in database for user ' . $user->getUserName($aData['account_id']));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
require_once('cron_end.inc.php');
|
||||
?>
|
||||
@ -13,7 +13,7 @@ PHP_BIN=$( which php )
|
||||
PIDFILE='/tmp/mmcfe-ng-cron.pid'
|
||||
|
||||
# List of cruns to execute
|
||||
CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php auto_payout.php tickerupdate.php notifications.php statistics.php archive_cleanup.php"
|
||||
CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php manual_payout.php auto_payout.php tickerupdate.php notifications.php statistics.php archive_cleanup.php"
|
||||
|
||||
# Output additional runtime information
|
||||
VERBOSE="0"
|
||||
|
||||
@ -26,6 +26,7 @@ require_once(CLASS_DIR . '/api.class.php');
|
||||
require_once(CLASS_DIR . '/mail.class.php');
|
||||
require_once(CLASS_DIR . '/tokentype.class.php');
|
||||
require_once(CLASS_DIR . '/token.class.php');
|
||||
require_once(CLASS_DIR . '/payout.class.php');
|
||||
require_once(CLASS_DIR . '/block.class.php');
|
||||
require_once(CLASS_DIR . '/setting.class.php');
|
||||
require_once(CLASS_DIR . '/monitoring.class.php');
|
||||
|
||||
@ -178,12 +178,12 @@ class Notification extends Mail {
|
||||
$stmt = $this->mysqli->prepare("SELECT account_id FROM $this->tableSettings WHERE type = ? AND active = 1 AND account_id = ?");
|
||||
if ($stmt && $stmt->bind_param('si', $strType, $account_id) && $stmt->execute() && $stmt->bind_result($id) && $stmt->fetch()) {
|
||||
if ($stmt->close() && $this->sendMail('notifications/' . $strType, $aMailData) && $this->addNotification($account_id, $strType, $aMailData)) {
|
||||
$this->setErrorMessage('Error sending mail notification');
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$this->setErrorMessage('User disabled ' . $strType . ' notifications');
|
||||
}
|
||||
$this->setErrorMessage('Error sending mail notification');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
65
public/include/classes/payout.class.php
Normal file
65
public/include/classes/payout.class.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
// Make sure we are called from index.php
|
||||
if (!defined('SECURITY')) die('Hacking attempt');
|
||||
|
||||
class Payout Extends Base {
|
||||
var $table = 'payouts';
|
||||
|
||||
/**
|
||||
* Check if the user has an active payout request already
|
||||
* @param account_id int Account ID
|
||||
* @return boolean bool True of False
|
||||
**/
|
||||
public function isPayoutActive($account_id) {
|
||||
$stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE completed = 0 AND account_id = ? LIMIT 1");
|
||||
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute( )&& $stmt->store_result() && $stmt->num_rows > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new payout request
|
||||
* @param account_id Account ID
|
||||
* @return data mixed Inserted ID or false
|
||||
**/
|
||||
public function createPayout($account_id=NULL) {
|
||||
$stmt = $this->mysqli->prepare("
|
||||
INSERT INTO $this->table (account_id)
|
||||
VALUES (?)
|
||||
");
|
||||
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute())
|
||||
return $stmt->insert_id;
|
||||
$this->setErrorMessage('Unable to create new payout request');
|
||||
$this->debug->append('Failed to create new payout request in database: ' . $this->mysqli->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a payout as processed
|
||||
* @param id int Payout ID
|
||||
* @return boolean bool True or False
|
||||
**/
|
||||
public function setProcessed($id) {
|
||||
$stmt = $this->mysqli->prepare("UPDATE $this->table SET completed = 1 WHERE id = ?");
|
||||
if ($stmt && $stmt->bind_param('i', $id) && $stmt->execute())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$oPayout = new Payout();
|
||||
$oPayout->setDebug($debug);
|
||||
$oPayout->setMysql($mysqli);
|
||||
@ -10,55 +10,22 @@ if ($user->isAuthenticated()) {
|
||||
} else {
|
||||
switch (@$_POST['do']) {
|
||||
case 'cashOut':
|
||||
if ($setting->getValue('manual_payout_active') == 1) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'A manual payout is in progress. Please try again later.', 'TYPE' => 'errormsg');
|
||||
} else if ($setting->getValue('disable_mp') == 1) {
|
||||
if ($setting->getValue('disable_mp') == 1) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Manual payouts are disabled.', 'TYPE' => 'info');
|
||||
} else {
|
||||
$setting->setValue('manual_payout_active', 1);
|
||||
$continue = true;
|
||||
$aBalance = $transaction->getBalance($_SESSION['USERDATA']['id']);
|
||||
$dBalance = $aBalance['confirmed'];
|
||||
$sCoinAddress = $user->getCoinAddress($_SESSION['USERDATA']['id']);
|
||||
// Ensure we can cover the potential transaction fee
|
||||
if ($dBalance > $config['txfee']) {
|
||||
if ($bitcoin->can_connect() === true) {
|
||||
try {
|
||||
$bitcoin->validateaddress($sCoinAddress);
|
||||
} catch (BitcoinClientException $e) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Invalid payment address: ' . $sUserSendAddress, 'TYPE' => 'errormsg');
|
||||
$continue = false;
|
||||
}
|
||||
if ($continue == true) {
|
||||
// Send balance to address, mind fee for transaction!
|
||||
try {
|
||||
$auto_payout = $monitoring->getStatus('auto_payout_active');
|
||||
if ($auto_payout['value'] == 0) {
|
||||
$bitcoin->sendtoaddress($sCoinAddress, $dBalance);
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Auto-payout active, please contact site support immidiately to revoke invalid transactions.', 'TYPE' => 'errormsg');
|
||||
$continue = false;
|
||||
}
|
||||
} catch (BitcoinClientException $e) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send ' . $config['currency'] . ', please contact site support immidiately', 'TYPE' => 'errormsg');
|
||||
$continue = false;
|
||||
}
|
||||
}
|
||||
// Set balance to 0, add to paid out, insert to ledger
|
||||
if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance - $config['txfee'], 'Debit_MP', NULL, $sCoinAddress) && $transaction->addTransaction($_SESSION['USERDATA']['id'], $config['txfee'], 'TXFee', NULL, $sCoinAddress) ) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Transaction completed', 'TYPE' => 'success');
|
||||
$aMailData['email'] = $user->getUserEmail($user->getUserName($_SESSION['USERDATA']['id']));
|
||||
$aMailData['amount'] = $dBalance;
|
||||
$aMailData['subject'] = 'Manual Payout Completed';
|
||||
$notification->sendNotification($_SESSION['USERDATA']['id'], 'manual_payout', $aMailData);
|
||||
if (!$oPayout->isPayoutActive($_SESSION['USERDATA']['id'])) {
|
||||
if ($iPayoutId = $oPayout->createPayout($_SESSION['USERDATA']['id'])) {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Created new manual payout request with ID #' . $iPayoutId);
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to create manual payout request.', 'TYPE' => 'errormsg');
|
||||
}
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service', 'TYPE' => 'errormsg');
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'You already have one active manual payout request.', 'TYPE' => 'errormsg');
|
||||
}
|
||||
} else {
|
||||
$_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee'] . ' ' . $config['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg');
|
||||
}
|
||||
$setting->setValue('manual_payout_active', 0);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@ -27,6 +27,14 @@ $aCronStatus = array(
|
||||
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('auto_payout_endtime') ),
|
||||
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('auto_payout_message') ),
|
||||
),
|
||||
'manual_payout' => array (
|
||||
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('manual_payout_status') ),
|
||||
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('manual_payout_active') ),
|
||||
array( 'NAME' => 'Runtime', 'STATUS' => $monitoring->getStatus('manual_payout_runtime') ),
|
||||
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('manual_payout_starttime') ),
|
||||
array( 'NAME' => 'Last Run', 'STATUS' => $monitoring->getStatus('manual_payout_endtime') ),
|
||||
array( 'NAME' => 'Last Message', 'STATUS' => $monitoring->getStatus('manual_payout_message') ),
|
||||
),
|
||||
'archive_cleanup' => array (
|
||||
array( 'NAME' => 'Exit Code', 'STATUS' => $monitoring->getStatus('archive_cleanup_status') ),
|
||||
array( 'NAME' => 'Active', 'STATUS' => $monitoring->getStatus('archive_cleanup_active') ),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user