A somewhat comicotragic guide on how to interact with the plugin’s ledger.
Do you even PHP, bro? Yes? I’m sorry to hear that. 😢 Well, OK then, this article is for you. Read on to learn how to create and manipulate transactions in Bitcoin and Altcoin Wallets from your PHP code. The article includes links to version
4.4.8 of the plugin’s code, which is the current version at this time.
The MySQL ledger
The plugin maintains a ledger of transactions in the
wp_wallets_txs MySQL table. User balances are calculated as the sum of all user transactions.
The way this is done resembles my life: it’s sad and depressing. Unfortunately, for incoming transactions, if you specify a transaction fee, the amount added to the balance is the amount minus the fee. But for transactions that reduce the user’s balance, the amount taken away from the balance is the specified amount, and the recipient receives the amount minus the fee. Please don’t ask why.
Equally sad is the way that transactions are stored in the database. I’m sorry about this. Transactions can be in one of the following categories: deposits, withdrawals, internal transfers (move) and trades. Trades are there for facilitating the Exchange extension.
The PHP API
Please don’t despair yet, there’s a silver lining among all of this sadness:
If you want to initiate a withdrawal or internal transfer from your PHP code, you can use a convenient API based on WordPress actions. You can find documentation and examples on this PHPdoc site for issuing withdrawals and internal transfers. This is the recommended way to initiate withdrawals or internal transfers from PHP.
You can decide whether you want the plugin to skip admin/user verification or not, and whether you want the plugin to skip checking the user for the right WordPress capabilities. This overrides any admin settings.
Understanding transaction states
Coin adapters were my pathetic, misguided attempt at abstracting away the details of each wallet back-end. Not a day goes by that I don’t lament some of the design decisions about them. 😭
Coin adapters discover incoming deposits and inform the plugin about their status. Coin adapters also monitor outgoing withdrawals. This is needed because in most blockchains, transactions have network confirmations and the plugin needs to monitor these. Transactions can be in one of the following states:
unconfirmedThis is the initial state for transactions. Depending on admin settings, transactions may have to be verified by the user over email, and/or by an admin in the Transactions screen.
pendingThe cron job will regularly scan transactions to see if they have the necessary verifications (see above). If they do, it progresses them to a
failedA transaction will be attempted for a pre-designated number of times (default: three attempts for withdrawals, one for internal transfers). When the number of retries left becomes zero (incidentally, this is also the number of friends I have), a transaction state becomes failed. This means that in every attempt the plugin made to execute the transaction, the result was, like my career, a failure. Maybe the wallet is down, or maybe the wallet’s fees were set too low, or maybe the user doesn’t have the balance required any more, or something else broke. The plugin will not bother to try and execute the transaction any more, because, what’s the point even? The user will receive a cold, dry automated email from the plugin, letting him know that the result of executing this transaction was, like most things in life, a big disappointment. If the user is lucky, they will at least receive a meaningful error message in that email.
cancelledThis is the state for transactions that the admin has cancelled from the Transactions admin screen. Additionally, if the admin has the “Cancel old unconfirmed/pending transactions” feature turned on, and a transaction remains without verification, it will be automatically cancelled after the designated time interval.
doneCongratulations, your transaction went through and is now affecting the user’s balance. Well, three cheers for you, I hope you are happy now. Don’t get too excited though; you’ll eventually find that every success is fleeting and transient, and therefore ultimately unfulfilling.
A more direct approach
The PHP API not doing it for you? I hear ya!
If you want to do anything more advanced than simple transaction placement, the PHP API kinda sucks. Here’s how to issue transactions directly to the
wallets_transaction action. Get ready for a whole new level of sadness, pain and misery:
Creating a deposit transaction
You should probably only be doing this if you’re developing your own coin adapter. Let’s say you’ve discovered an incoming deposit transaction. Someone sent 20 of their hard-earned
XYZ coins to deposit address “
ADDRESS” via a transaction with id “
TRANSACTIONID” and they paid a fee of 1 coin to do this, so that only 19 coins are to be deposited. You would first trigger the
wallets_transaction action as follows:
$tx = new stdClass(); $tx->category = 'deposit'; $tx->symbol = 'XYZ'; $tx->txid = 'TRANSACTIONID'; $tx->address = 'ADDRESS'; $tx->extra = false; // this string is optional. only use it if the blockchain requires it. examples include the Monero "Payment ID", the XRP "Destination Tag", the SteemIt "Memo", etc. $tx->confirmations = 0; // this can be set to zero or whatever the current number of network confirmations is $tx->amount = 20; $tx->fee = 1; $tx->created_time = time(); // this can be the current time or an appropriate UNIX timestamp $tx->comment = 'whatever'; // this is optional, if you don't have anything useful to add as a comment, better say nothing at all do_action( 'wallets_transaction', $tx );
The plugin will check the address you specified against all user deposit addresses, both current and old. If an address matches, it will insert a deposit for that user into the DB. The plugin will also check the confirmation count against a threshold specified by the coin adapter. If the count is above the threshold, the status of the deposit is
done. If not, the transaction will appear as
pending. Pending deposits do not yet affect the user’s balance.
Updating a deposit transaction
If you thought that was it, guess again: You now have to do all of this again, every time the confirmation count of your transaction changes.
$tx object again as before, and update the
confirmations field to the new value. Trigger the action again and the plugin will update the
status columns in the
wp_wallets_txs table as appropriate. You can’t edit the other transaction data. If you need to do this, you will have to manipulate the database directly using the
Updating a withdraw transaction
If you’re simply trying to issue a withdrawal on the plugin’s ledger, use the
wallets_api_withdraw action from the PHP API. This will insert a new withdrawal into the DB.
If you are building a new coin adapter, then your life may be even sadder than mine. You will have to do whatever is needed to monitor the status of the withdrawal, so that confirmations are updated correctly. Because I feel sorry for you, I’ll try to help:
Every time you observe a change on the confirmation count of your withdrawal, you’ll need to construct an object, much like we did for deposits. Let’s say we observe on the blockchain that the transaction with “
TRANSACTIONID” now has 10 confirmations. Here’s how to let the plugin know:
$tx = new stdClass(); $tx->category = 'withdraw'; $tx->symbol = 'XYZ'; $tx->txid = 'TRANSACTIONID'; $tx->address = 'ADDRESS'; // the deposit address goes here $tx->extra = false; // this string is optional. only use it if the blockchain requires it. examples include the Monero "Payment ID", the XRP "Destination Tag", the SteemIt "Memo", etc. $tx->confirmations = 10; // this can be set to zero or whatever the current number of network confirmations is; here we'll assume it's 10 do_action( 'wallets_transaction', $tx );
The plugin will now modify the
status columns in the
wp_wallets_txs table as needed, in an existing withdraw row.
I’m sorry for everything
OK, if you read everything up to here, you probably need to re-evaluate your life. I know I will!
I need a drink.