Issues (431)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

include/classes/transaction.class.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
3
4
class Transaction extends Base {
5
  protected $table = 'transactions';
6
  public $num_rows = 0, $insert_id = 0;
7
8
  /**
9
   * Add a new transaction to our class table
10
   * We also store the inserted ID in case the user needs it
11
   * @param account_id int Account ID to book transaction for
12
   * @param amount float Coin amount
13
   * @param type string Transaction type [Credit, Debit_AP, Debit_MP, Fee, Donation, Orphan_Credit, Orphan_Fee, Orphan_Donation]
14
   * @param block_id int Block ID to link transaction to [optional]
15
   * @param coin_address string Coin address for this transaction [optional]
16
   * @return bool
17
   **/
18
  public function addTransaction($account_id, $amount, $type='Credit', $block_id=NULL, $coin_address=NULL, $txid=NULL) {
19
    $amount = number_format($amount, $this->coin->getCoinValuePrevision(), '.', '');
20
    $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, amount, block_id, type, coin_address, txid) VALUES (?, ?, ?, ?, ?, ?)");
21
    if ($this->checkStmt($stmt) && $stmt->bind_param("isisss", $account_id, $amount, $block_id, $type, $coin_address, $txid) && $stmt->execute()) {
22
      $this->insert_id = $stmt->insert_id;
23
      return true;
24
    }
25
    return $this->sqlError();
26
  }
27
28
  /**
29
   * Update a transaction with a RPC transaction ID
30
   * @param id integer Transaction ID
31
   * @param txid string RPC Transaction Identifier
32
   * @return bool true or false
33
   **/
34
  public function setRPCTxId($transaction_id, $rpc_txid=NULL) {
35
    $stmt = $this->mysqli->prepare("UPDATE $this->table SET txid = ? WHERE id = ?");
36
    if ($this->checkStmt($stmt) && $stmt->bind_param('si', $rpc_txid, $transaction_id) && $stmt->execute())
37
      return true;
38
    return $this->sqlError();
39
  }
40
41
  /*
42
   * Mark transactions of a user as archived
43
   * @param account_id int Account ID
44
   * @param txid int Transaction ID to start from
45
   * @param bool boolean True or False
46
   **/
47 View Code Duplication
  public function setArchived($account_id, $txid) {
48
    // Update all paid out transactions as archived
49
    $stmt = $this->mysqli->prepare("
50
      UPDATE $this->table AS t
51
      LEFT JOIN " . $this->block->getTableName() . " AS b
52
      ON b.id = t.block_id
53
      SET t.archived = 1
54
      WHERE t.archived = 0
55
      AND (
56
           ( t.account_id = ? AND t.id <= ? AND b.confirmations >= ? )
57
        OR ( t.account_id = ? AND t.id <= ? AND b.confirmations = -1 )
58
        OR ( t.account_id = ? AND t.id <= ? AND t.type IN ( 'Credit_PPS', 'Donation_PPS', 'Fee_PPS', 'TXFee', 'Debit_MP', 'Debit_AP' ) )
59
      )");
60
     if ($this->checkStmt($stmt) && $stmt->bind_param('iiiiiii', $account_id, $txid, $this->config['confirmations'], $account_id, $txid, $account_id, $txid) && $stmt->execute())
61
      return true;
62
    return $this->sqlError();
63
  }
64
65
  /**
66
   * Fetch a transaction summary by type with total amounts
67
   * @param account_id int Account ID, NULL for all
68
   * @return data array type and total
69
   **/
70
  public function getTransactionSummary($account_id=NULL) {
71
    if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data;
72
    $sql = "
73
      SELECT
74
        SUM(t.amount) AS total, t.type AS type
75
      FROM $this->table AS t
76
      LEFT OUTER JOIN " . $this->block->getTableName() . " AS b
77
      ON b.id = t.block_id
78
      WHERE ( b.confirmations > 0 OR b.id IS NULL )";
79
    if (!empty($account_id)) {
80
      $sql .= " AND t.account_id = ? ";
81
      $this->addParam('i', $account_id);
82
    }
83
    $sql .= " GROUP BY t.type";
84
    $stmt = $this->mysqli->prepare($sql);
85
    if (!empty($account_id)) {
86 View Code Duplication
      if (!($this->checkStmt($stmt) && call_user_func_array( array($stmt, 'bind_param'), $this->getParam()) && $stmt->execute()))
87
        return false;
88
      $result = $stmt->get_result();
89
    } else {
90
      if (!($this->checkStmt($stmt) && $stmt->execute()))
91
        return false;
92
      $result = $stmt->get_result();
93
    }
94
    if ($result) {
95
      $aData = NULL;
96
      while ($row = $result->fetch_assoc()) {
97
        $aData[$row['type']] = $row['total'];
98
      }
99
      // Cache data for a while, query takes long on many rows
100
      return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData, 60);
101
    }
102
    return $this->sqlError();
103
  }
104
105
106
  /**
107
   * Fetch a transaction summary by user with total amounts
108
   * @param account_id int Account ID, NULL for all
109
   * @return data array type and total
110
   **/
111 View Code Duplication
  public function getTransactionTypebyTime($account_id=NULL) {
112
    $this->debug->append("STA " . __METHOD__, 4);
113
    if ($data = $this->memcache->get(__FUNCTION__)) return $data;
114
    $stmt = $this->mysqli->prepare("
115
      SELECT
116
        IFNULL(SUM(IF(t.type = 'Credit' AND timestamp >= DATE_SUB(now(), INTERVAL 3600 SECOND), t.amount, 0)), 0) AS 1HourCredit,
117
        IFNULL(SUM(IF(t.type = 'Bonus' AND timestamp >= DATE_SUB(now(), INTERVAL 3600 SECOND), t.amount, 0)), 0) AS 1HourBonus,
118
        IFNULL(SUM(IF(t.type = 'Debit_MP' AND timestamp >= DATE_SUB(now(), INTERVAL 3600 SECOND), t.amount, 0)), 0) AS 1HourDebitMP,
119
        IFNULL(SUM(IF(t.type = 'Debit_AP' AND timestamp >= DATE_SUB(now(), INTERVAL 3600 SECOND), t.amount, 0)), 0) AS 1HourDebitAP,
120
        IFNULL(SUM(IF(t.type = 'TXFee' AND timestamp >= DATE_SUB(now(), INTERVAL 3600 SECOND), t.amount, 0)), 0) AS 1HourTXFee,
121
        IFNULL(SUM(IF(t.type = 'Fee' AND timestamp >= DATE_SUB(now(), INTERVAL 3600 SECOND), t.amount, 0)), 0) AS 1HourFee,
122
        IFNULL(SUM(IF(t.type = 'Donation' AND timestamp >= DATE_SUB(now(), INTERVAL 3600 SECOND), t.amount, 0)), 0) AS 1HourDonation,
123
124
        IFNULL(SUM(IF(t.type = 'Credit' AND timestamp >= DATE_SUB(now(), INTERVAL 86400 SECOND), t.amount, 0)), 0) AS 24HourCredit,
125
        IFNULL(SUM(IF(t.type = 'Bonus' AND timestamp >= DATE_SUB(now(), INTERVAL 86400 SECOND), t.amount, 0)), 0) AS 24HourBonus,
126
        IFNULL(SUM(IF(t.type = 'Debit_MP' AND timestamp >= DATE_SUB(now(), INTERVAL 86400 SECOND), t.amount, 0)), 0) AS 24HourDebitMP,
127
        IFNULL(SUM(IF(t.type = 'Debit_AP' AND timestamp >= DATE_SUB(now(), INTERVAL 86400 SECOND), t.amount, 0)), 0) AS 24HourDebitAP,
128
        IFNULL(SUM(IF(t.type = 'TXFee' AND timestamp >= DATE_SUB(now(), INTERVAL 86400 SECOND), t.amount, 0)), 0) AS 24HourTXFee,
129
        IFNULL(SUM(IF(t.type = 'Fee' AND timestamp >= DATE_SUB(now(), INTERVAL 86400 SECOND), t.amount, 0)), 0) AS 24HourFee,
130
        IFNULL(SUM(IF(t.type = 'Donation' AND timestamp >= DATE_SUB(now(), INTERVAL 86400 SECOND), t.amount, 0)), 0) AS 24HourDonation,
131
132
        IFNULL(SUM(IF(t.type = 'Credit' AND timestamp >= DATE_SUB(now(), INTERVAL 604800 SECOND), t.amount, 0)), 0) AS 1WeekCredit,
133
        IFNULL(SUM(IF(t.type = 'Bonus' AND timestamp >= DATE_SUB(now(), INTERVAL 604800 SECOND), t.amount, 0)), 0) AS 1WeekBonus,
134
        IFNULL(SUM(IF(t.type = 'Debit_MP' AND timestamp >= DATE_SUB(now(), INTERVAL 604800 SECOND), t.amount, 0)), 0) AS 1WeekDebitMP,
135
        IFNULL(SUM(IF(t.type = 'Debit_AP' AND timestamp >= DATE_SUB(now(), INTERVAL 604800 SECOND), t.amount, 0)), 0) AS 1WeekDebitAP,
136
        IFNULL(SUM(IF(t.type = 'TXFee' AND timestamp >= DATE_SUB(now(), INTERVAL 604800 SECOND), t.amount, 0)), 0) AS 1WeekTXFee,
137
        IFNULL(SUM(IF(t.type = 'Fee' AND timestamp >= DATE_SUB(now(), INTERVAL 604800 SECOND), t.amount, 0)), 0) AS 1WeekFee,
138
        IFNULL(SUM(IF(t.type = 'Donation' AND timestamp >= DATE_SUB(now(), INTERVAL 604800 SECOND), t.amount, 0)), 0) AS 1WeekDonation,
139
140
        IFNULL(SUM(IF(t.type = 'Credit' AND timestamp >= DATE_SUB(now(), INTERVAL 2419200 SECOND), t.amount, 0)), 0) AS 1MonthCredit,
141
        IFNULL(SUM(IF(t.type = 'Bonus' AND timestamp >= DATE_SUB(now(), INTERVAL 2419200 SECOND), t.amount, 0)), 0) AS 1MonthBonus,
142
        IFNULL(SUM(IF(t.type = 'Debit_MP' AND timestamp >= DATE_SUB(now(), INTERVAL 2419200 SECOND), t.amount, 0)), 0) AS 1MonthDebitMP,
143
        IFNULL(SUM(IF(t.type = 'Debit_AP' AND timestamp >= DATE_SUB(now(), INTERVAL 2419200 SECOND), t.amount, 0)), 0) AS 1MonthDebitAP,
144
        IFNULL(SUM(IF(t.type = 'TXFee' AND timestamp >= DATE_SUB(now(), INTERVAL 2419200 SECOND), t.amount, 0)), 0) AS 1MonthTXFee,
145
        IFNULL(SUM(IF(t.type = 'Fee' AND timestamp >= DATE_SUB(now(), INTERVAL 2419200 SECOND), t.amount, 0)), 0) AS 1MonthFee,
146
        IFNULL(SUM(IF(t.type = 'Donation' AND timestamp >= DATE_SUB(now(), INTERVAL 2419200 SECOND), t.amount, 0)), 0) AS 1MonthDonation,
147
148
        IFNULL(SUM(IF(t.type = 'Credit' AND timestamp >= DATE_SUB(now(), INTERVAL 31536000 SECOND), t.amount, 0)), 0) AS 1YearCredit,
149
        IFNULL(SUM(IF(t.type = 'Bonus' AND timestamp >= DATE_SUB(now(), INTERVAL 31536000 SECOND), t.amount, 0)), 0) AS 1YearBonus,
150
        IFNULL(SUM(IF(t.type = 'Debit_MP' AND timestamp >= DATE_SUB(now(), INTERVAL 31536000 SECOND), t.amount, 0)), 0) AS 1YearDebitMP,
151
        IFNULL(SUM(IF(t.type = 'Debit_AP' AND timestamp >= DATE_SUB(now(), INTERVAL 31536000 SECOND), t.amount, 0)), 0) AS 1YearDebitAP,
152
        IFNULL(SUM(IF(t.type = 'TXFee' AND timestamp >= DATE_SUB(now(), INTERVAL 31536000 SECOND), t.amount, 0)), 0) AS 1YearTXFee,
153
        IFNULL(SUM(IF(t.type = 'Fee' AND timestamp >= DATE_SUB(now(), INTERVAL 31536000 SECOND), t.amount, 0)), 0) AS 1YearFee,
154
        IFNULL(SUM(IF(t.type = 'Donation' AND timestamp >= DATE_SUB(now(), INTERVAL 31536000 SECOND), t.amount, 0)), 0) AS 1YearDonation
155
      FROM $this->table AS t
156
      LEFT OUTER JOIN " . $this->block->getTableName() . " AS b ON b.id = t.block_id
157
      WHERE
158
        t.account_id = ? AND (b.confirmations > 0 OR b.id IS NULL)");
159
    if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result())
160
    	return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_assoc(), 60);
161
    return $this->sqlError();
162
  }
163
164
  /**
165
   * Get all transactions from start for account_id
166
   * @param start int Starting point, id of transaction
167
   * @param filter array Filter to limit transactions
168
   * @param limit int Only display this many transactions
169
   * @param account_id int Account ID
170
   * @return data array Database fields as defined in SELECT
171
   **/
172
  public function getTransactions($start=0, $filter=NULL, $limit=30, $account_id=NULL) {
173
    $this->debug->append("STA " . __METHOD__, 4);
174
    $sql = "
175
      SELECT
176
        t.id AS id,
177
        a.username as username,
178
        t.type AS type,
179
        t.amount AS amount,
180
        t.coin_address AS coin_address,
181
        t.timestamp AS timestamp,
182
        t.txid AS txid,
183
        b.height AS height,
184
        b.blockhash AS blockhash,
185
        b.confirmations AS confirmations
186
      FROM $this->table AS t
187
      LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id
188
      LEFT JOIN " . $this->user->getTableName() . " AS a ON t.account_id = a.id";
189
    if (!empty($account_id)) {
190
      $sql .= " WHERE ( t.account_id = ? ) ";
191
      $this->addParam('i', $account_id);
192
    }
193
    if (is_array($filter)) {
194
      $aFilter = array();
195
      foreach ($filter as $key => $value) {
196
        if (!empty($value)) {
197
          switch ($key) {
198
          case 'type':
199
            $aFilter[] = "( t.type = ? )";
200
            $this->addParam('s', $value);
201
            break;
202
          case 'status':
203
            switch ($value) {
204
            case 'Confirmed':
205
              if (empty($filter['type']) || ($filter['type'] != 'Debit_AP' && $filter['type'] != 'Debit_MP' && $filter['type'] != 'TXFee' && $filter['type'] != 'Credit_PPS' && $filter['type'] != 'Fee_PPS' && $filter['type'] != 'Donation_PPS')) {
206
                $aFilter[] = "( b.confirmations >= " . $this->config['confirmations'] . " OR ISNULL(b.confirmations) )";
207
              }
208
                break;
209
            case 'Unconfirmed':
210
              $aFilter[] = "( b.confirmations < " . $this->config['confirmations'] . " AND b.confirmations >= 0 )";
211
                break;
212
            case 'Orphan':
213
              $aFilter[] = "( b.confirmations = -1 )";
214
                break;
215
            }
216
            break;
217
            case 'account':
218
              $aFilter[] = "( LOWER(a.username) = LOWER(?) )";
219
              $this->addParam('s', $value);
220
              break;
221
            case 'address':
222
              $aFilter[] = "( t.coin_address = ? )";
223
              $this->addParam('s', $value);
224
              break;
225
          }
226
        }
227
      }
228
      if (!empty($aFilter)) {
229
      	empty($account_id) ? $sql .= " WHERE " : $sql .= " AND ";
230
        $sql .= implode(' AND ', $aFilter);
231
      }
232
    }
233
    $sql .= " ORDER BY id DESC LIMIT ?,?";
234
    // Add some other params to query
235
    $this->addParam('i', $start);
236
    $this->addParam('i', $limit);
237
    $stmt = $this->mysqli->prepare($sql);
238
    if ($this->checkStmt($stmt) && call_user_func_array( array($stmt, 'bind_param'), $this->getParam()) && $stmt->execute() && $result = $stmt->get_result())
239
      return $result->fetch_all(MYSQLI_ASSOC);
240
    return $this->sqlError();
241
  }
242
243
  /**
244
   * Get all different transaction types
245
   * @return mixed array/bool Return types on succes, false on failure
246
   **/
247
  public function getTypes() {
248
    $stmt = $this->mysqli->prepare("SELECT DISTINCT type FROM $this->table");
249 View Code Duplication
    if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) {
250
      $aData = array('' => '');
251
      while ($row = $result->fetch_assoc()) {
252
        $aData[$row['type']] = $row['type'];
253
      }
254
      return $aData;
255
    }
256
    return $this->sqlError();
257
  }
258
259
  /**
260
   * Get all donation transactions
261
   * Used on donors page
262
   * return data array Donors and amounts
263
   **/
264
  public function getDonations() {
265
    $this->debug->append("STA " . __METHOD__, 4);
266
    $stmt = $this->mysqli->prepare("
267
      SELECT
268
        SUM(t.amount) AS donation,
269
        a.username AS username,
270
        a.is_anonymous AS is_anonymous,
271
        ROUND(a.donate_percent, 2) AS donate_percent
272
      FROM $this->table AS t
273
      LEFT JOIN " . $this->user->getTableName() . " AS a
274
      ON t.account_id = a.id
275
      LEFT JOIN " . $this->block->getTableName() . " AS b
276
      ON t.block_id = b.id
277
      WHERE
278
      (
279
        ( t.type = 'Donation' AND b.confirmations >= " . $this->config['confirmations'] . " ) OR
280
        t.type = 'Donation_PPS'
281
      )
282
      GROUP BY a.username
283
      ORDER BY donation DESC
284
      ");
285
    if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
286
      return $result->fetch_all(MYSQLI_ASSOC);
287
    return $this->sqlError();
288
  }
289
290
  /**
291
   * Get total balance for all users locked in wallet
292
   * This includes any outstanding unconfirmed transactions!
293
   * @param none
294
   * @return data double Amount locked for users
295
   **/
296
  public function getLockedBalance() {
297
    $this->debug->append("STA " . __METHOD__, 4);
298
    $stmt = $this->mysqli->prepare("
299
      SELECT
300
        (
301
          SUM( IF( ( t.type IN ('Credit','Bonus') AND b.confirmations >= ? ) OR t.type = 'Credit_PPS', t.amount, 0 ) ) -
302
          SUM( IF( t.type IN ('Debit_MP', 'Debit_AP'), t.amount, 0 ) ) -
303
          SUM( IF( ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ), t.amount, 0 ) )
304
        ) AS balance
305
      FROM $this->table AS t
306
      LEFT JOIN " . $this->block->getTableName() . " AS b
307
      ON t.block_id = b.id
308
      WHERE archived = 0");
309 View Code Duplication
    if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $this->config['confirmations'], $this->config['confirmations']) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch())
310
      return $dBalance;
311
    return $this->sqlError();
312
  }
313
314
  /**
315
   * Get an accounts total balance, ignore archived entries
316
   * @param account_id int Account ID
317
   * @return data float Credit - Debit - Fees - Donation
318
   **/
319
  public function getBalance($account_id) {
320
    $this->debug->append("STA " . __METHOD__, 4);
321
    $stmt = $this->mysqli->prepare("
322
      SELECT
323
        IFNULL((
324
          SUM( IF( ( t.type IN ('Credit','Bonus') AND b.confirmations >= ? ) OR t.type = 'Credit_PPS', t.amount, 0 ) ) -
325
          SUM( IF( t.type IN ('Debit_MP', 'Debit_AP'), t.amount, 0 ) ) -
326
          SUM( IF( ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ), t.amount, 0 ) )
327
        ), 0) AS confirmed,
328
        IFNULL((
329
          SUM( IF( t.type IN ('Credit','Bonus') AND b.confirmations < ? AND b.confirmations >= 0, t.amount, 0 ) ) -
330
          SUM( IF( t.type IN ('Donation','Fee') AND b.confirmations < ? AND b.confirmations >= 0, t.amount, 0 ) )
331
        ), 0) AS unconfirmed,
332
        IFNULL((
333
          SUM( IF( t.type IN ('Credit','Bonus') AND b.confirmations = -1, t.amount, 0) ) -
334
          SUM( IF( t.type IN ('Donation','Fee') AND b.confirmations = -1, t.amount, 0) )
335
        ), 0) AS orphaned
336
      FROM $this->table AS t
337
      LEFT JOIN " . $this->block->getTableName() . " AS b
338
      ON t.block_id = b.id
339
      WHERE t.account_id = ?
340
      AND archived = 0
341
      ");
342
    if ($this->checkStmt($stmt) && $stmt->bind_param("iiiii", $this->config['confirmations'], $this->config['confirmations'], $this->config['confirmations'], $this->config['confirmations'], $account_id) && $stmt->execute() && $result = $stmt->get_result())
343
      return $result->fetch_assoc();
344
    return $this->sqlError();
345
  }
346
347
  /**
348
   * Get our Auto Payout queue
349
   * @param none
350
   * @return data array Account settings and confirmed balances
351
   **/
352
  public function getAPQueue($limit=250) {
353
    $this->debug->append("STA " . __METHOD__, 4);
354
    $stmt = $this->mysqli->prepare("
355
      SELECT
356
        a.id,
357
        a.username,
358
        ca.ap_threshold,
359
        ca.coin_address,
360
        IFNULL(
361
            (
362
              SUM( IF( ( t.type IN ('Credit','Bonus') AND b.confirmations >= " . $this->config['confirmations'] . ") OR t.type = 'Credit_PPS', t.amount, 0 ) ) -
363
              SUM( IF( t.type IN ('Debit_MP', 'Debit_AP'), t.amount, 0 ) ) -
364
              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 ) )
365
          ), 0
366
        ) AS confirmed
367
      FROM $this->table AS t
368
      LEFT JOIN " . $this->block->getTableName() . " AS b
369
      ON t.block_id = b.id
370
      LEFT JOIN " . $this->user->getTableName() . " AS a
371
      ON t.account_id = a.id
372
      LEFT JOIN " . $this->coin_address->getTableName() . " AS ca
373
      ON ca.account_id = a.id
374
      WHERE t.archived = 0 AND ca.ap_threshold > 0 AND ca.coin_address IS NOT NULL AND ca.coin_address != '' AND ca.currency = ?
375
      GROUP BY t.account_id
376
      HAVING confirmed > ca.ap_threshold AND confirmed > " . $this->config['txfee_auto'] . "
377
      LIMIT ?");
378 View Code Duplication
    if ($this->checkStmt($stmt) && $stmt->bind_param('si', $this->config['currency'], $limit) && $stmt->execute() && $result = $stmt->get_result())
379
      return $result->fetch_all(MYSQLI_ASSOC);
380
    return $this->sqlError();
381
  }
382
383
384
  /**
385
   * Debit a user account
386
   * @param account_id int Account ID
387
   * @param coin_address string Coin Address
388
   * @param amount float Balance to record
389
   * @return int Debit transaction ID or false
390
   **/
391
  public function createDebitMPRecord($account_id, $coin_address, $amount) {
392
    return $this->createDebitRecord($account_id, $coin_address, $amount, 'Debit_MP');
393
  }
394
  public function createDebitAPRecord($account_id, $coin_address, $amount) {
395
    return $this->createDebitRecord($account_id, $coin_address, $amount, 'Debit_AP');
396
  }
397
  private function createDebitRecord($account_id, $coin_address, $amount, $type) {
398
    // Calculate and deduct txfee from amount
399
    $type == 'Debit_MP' ? $txfee = $this->config['txfee_manual'] : $txfee = $this->config['txfee_auto'];
400
    $amount = $amount - $txfee;
401
    // Add Debit record
402 View Code Duplication
    if (!$this->addTransaction($account_id, $amount, $type, NULL, $coin_address, NULL)) {
403
      $this->setErrorMessage('Failed to create ' . $type . ' transaction record in database');
404
      return false;
405
    }
406
    // Fetch the inserted record ID so we can return this at the end
407
    $transaction_id = $this->insert_id;
408
    // Add TXFee record
409 View Code Duplication
    if ($txfee > 0) {
410
      if (!$this->addTransaction($account_id, $txfee, 'TXFee', NULL, $coin_address)) {
411
        $this->setErrorMessage('Failed to create TXFee transaction record in database: ' . $this->getError());
412
        return false;
413
      }
414
    }
415
    // Mark transactions archived
416
    if (!$this->setArchived($account_id, $this->insert_id)) {
417
      $this->setErrorMessage('Failed to mark transactions <= #' . $this->insert_id . ' as archived. ERROR: ' . $this->getError());
418
      return false;
419
    }
420
    // Recheck the users balance to make sure it is now 0
421
    if (!$aBalance = $this->getBalance($account_id)) {
422
      $this->setErrorMessage('Failed to fetch balance for account ' . $account_id . '. ERROR: ' . $this->getCronError());
423
      return false;
424
    }
425
    if ($aBalance['confirmed'] > 0) {
426
      $this->setErrorMessage('User has a remaining balance of ' . $aBalance['confirmed'] . ' after a successful payout!');
427
      return false;
428
    }
429
    // Notify user via  mail
430
    $aMailData['email'] = $this->user->getUserEmailById($account_id);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$aMailData was never initialized. Although not strictly required by PHP, it is generally a good practice to add $aMailData = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
431
    $aMailData['subject'] = $type . ' Completed';
432
    $aMailData['amount'] = $amount;
433
    $aMailData['currency'] = $this->config['currency'];
434
    if (!$this->notification->sendNotification($account_id, 'payout', $aMailData)) {
435
      $this->setErrorMessage('Failed to send notification email to users address: ' . $aMailData['email'] . 'ERROR: ' . $this->notification->getCronError());
436
    }
437
    return $transaction_id;
438
  }
439
440
  /**
441
   * Get all new, unprocessed manual payout requests
442
   * @param none
443
   * @return data Associative array with DB Fields
444
   **/
445
  public function getMPQueue($limit=250) {
446
    $stmt = $this->mysqli->prepare("
447
      SELECT
448
      a.id,
449
      a.username,
450
      ca.ap_threshold,
451
      ca.coin_address,
452
      p.id AS payout_id,
453
      IFNULL(
454
          (
455
            SUM( IF( ( t.type IN ('Credit','Bonus') AND b.confirmations >= " . $this->config['confirmations'] . ") OR t.type = 'Credit_PPS', t.amount, 0 ) ) -
456
            SUM( IF( t.type IN ('Debit_MP', 'Debit_AP'), t.amount, 0 ) ) -
457
            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 ) )
458
        ), 0
459
      ) AS confirmed
460
      FROM " . $this->payout->getTableName() . " AS p
461
      JOIN " . $this->user->getTableName() . " AS a
462
      ON p.account_id = a.id
463
      JOIN " . $this->getTableName() . " AS t
464
      ON t.account_id = p.account_id
465
      LEFT JOIN " . $this->block->getTableName() . " AS b
466
      ON t.block_id = b.id
467
      LEFT JOIN " . $this->coin_address->getTableName() . " AS ca
468
      ON ca.account_id = a.id
469
      WHERE p.completed = 0 AND t.archived = 0 AND ca.currency = ? AND ca.coin_address IS NOT NULL AND ca.coin_address != ''
470
      GROUP BY t.account_id
471
      HAVING confirmed > " . $this->config['txfee_manual'] . "
472
      LIMIT ?");
473 View Code Duplication
    if ($this->checkStmt($stmt) && $stmt->bind_param('si', $this->config['currency'], $limit) && $stmt->execute() && $result = $stmt->get_result())
474
      return $result->fetch_all(MYSQLI_ASSOC);
475
    return $this->sqlError('E0050');
476
  }
477
}
478
479
$transaction = new Transaction();
480
$transaction->setMemcache($memcache);
481
$transaction->setNotification($notification);
482
$transaction->setSetting($setting);
483
$transaction->setDebug($debug);
484
$transaction->setCoin($coin);
485
$transaction->setCoinAddress($coin_address);
486
$transaction->setMysql($mysqli);
487
$transaction->setConfig($config);
488
$transaction->setBlock($block);
489
$transaction->setUser($user);
490
$transaction->setPayout($oPayout);
491
$transaction->setErrorCodes($aErrorCodes);
492