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); |
|
|
|
|
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
|
|
|
|
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: