? HOWTO: Create/update transactions from PHP
A somewhat comicotragic guide on how to interact with the plugin’s ledger.
The (admittedly sad) instructions below are for manipulating transactions on Bitcoin and Altcoin Wallets versions before 6.0.0
.
Since Bitcoin and Altcoin Wallets version 6.0.0
, the situation has improved. The `wp_wallets_txs` custom SQL table has been replaced by a `wallets_txs` Custom Post Type.
The class for managing this CPT is `[DSWallets\Transaction](https://wallets-phpdoc.dashed-slug.net/classes/DSWallets-Transaction.html)`. The PHPdoc for the class contains example code snippets showcasing how a Transaction can be created and manipulated.
To read more on how to create or edit transactions, in your WordPress admin screens, navigate to: Wallets Admin Docs → wallets → Developer reference → Working with custom post type objects: Wallets, Currencies, Addresses or Transactions.
You may continue using the Legacy PHP-API. To learn more about it, in your WordPress admin screens, navigate to: Wallets Admin Docs → wallets → Developer reference → Wallet APIs → Legacy PHP-API.
The Developer reference is also available on github.
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:
unconfirmed
This 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.pending
The cron job will regularly scan transactions to see if they have the necessary verifications (see above). If they do, it progresses them to apending
state.failed
A 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.cancelled
This 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.done
Congratulations, 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.
Construct the $tx
object again as before, and update the confirmations
field to the new value. Trigger the action again and the plugin will update the updated_time
, confirmations
, and 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 $wpdb
global.
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 updated_time
, confirmations
, and 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!
Please contact me on the forum or via email if you have any questions about the plugin, or if you simply need to vent your frustration. ?
I need a drink.
A drink is a good idea – it gives room for some thinking before coding, which in this case seems to be appropriate 🙂