Multicurrency wallets
Build multicurrency wallets with the Blnk Ledger.
A multi-currency wallet system is a system that manages balances and transactions between 2 or more asset classes. In this example, you’ll learn how to build a simple ledger that manages transaction workflows between multiple currencies.
We’ll learn about:
- Defining and creating your ledger structure
- Balance creation
- Moving money into wallets
- Moving money out of wallets
- Moving money between multicurrency wallets
- Best practices
1. Ledger structure
The entry point of the Blnk ledger system is ledger folders. These folders serve as a way to group and manage assets, accounts, and balances that fit your product or organization’s structure.
In this guide, we’ll use a simple structure:
- USD Ledger: Contains all USD wallets
- EUR Ledger: Contains all EUR wallets
The ledger structure is flexible and can be customized based on your specific needs. For instance, you could group by users instead of currencies, or use a combination of both.
See also:
Creating a USD Ledger
View your ledgers in your terminal:
Always store the ledger_id
in your database. You’ll need it for future operations related to this ledger.
Creating a EUR Ledger
It is the same process as the USD ledger creation. Only change the name
of the ledger and adjust the meta_data
as needed.
2. Balance (wallet) creation
Blnk uses the concept of ledger balances to manage accounts/balances in a ledger. In our example, we’ll create wallets for a customer named Jerry, who will have both a USD and EUR wallet.
See also:
Creating a USD Wallet for Customer A
View your balances in your terminal:
The balance_id
is important. Always store this in your database and associate it with the customer. You’ll use this id
for all future transactions involving this wallet.
Creating a EUR Wallet for Customer A
It is the same process as the USD wallet creation. Only change the ledger_id to the EUR ledger ID and set the currency to “EUR”.
3. Moving money into wallets
Once ledgers are created and balances/wallets have been set up for your customers, the next step is to start recording transactions.
In this example, we’ll cover funding our wallets with actual cash received from the outside world or other apps (banks/wallets etc). This section introduces key concepts in Blnk like:
- General Ledgers
- Overdrafts
meta_data
See also:
Funding Customer A's USD Wallet with $200.00
Field | Description |
---|---|
amount | The actual amount received, in this case 200.00 USD. |
precision | Blnk uses the concept of precision to accurately manage and store float amounts. For accuracy, always pass a precision to convert the original amount to its lowest denominator (without floats). In this case, to convert USD to cents, we use a precision of 100. So, 200.00 USD * 100 = 20000 cents. |
source | The source is part of Blnk’s implementation of the double-entry accounting principle. In this example, the amount was debited from the outside world (sender) and credited to one of our internal wallets. We use the concept of general ledgers to keep a record of all money coming from the outside world into our ledger. To easily do this without having to create a new ledger folder called general ledger (Blnk does this automatically), you can use the @ prefix followed by what you want to name the general ledger balance. In this case, it’s @World . You can name it anything, for example, @external-service-partner if you are receiving the payment from an external partner and want to track how much you’ve received or sent to the partner. |
destination | In this example, the destination is Customer A’s USD wallet, which we pass using the balance_id obtained when the balance was created: bln_e39a239a-a6ca-4509-b0d9-29dcc5630f8a. |
allow_overdraft | Since we’re tracking the general ledger balances (in this case, @World ), we’re essentially moving 200.00 USD from the @World balance to Customer A’s USD balance. But since in our application the @World balance is empty initially, we want to force an overdraft so the transaction goes through. If overdraft is not true, the transaction will be rejected with insufficient funds because the @World general ledger balance is empty at the beginning. Setting overdraft to true enables us to debit past the balance. |
meta_data | This field allows you to store additional information about the transaction. It’s crucial for reconciliation and auditing purposes. |
View your transactions in your terminal:
Always use the precision field to avoid floating-point arithmetic issues. For USD and EUR, a precision of 100 (representing cents) is typically used.
Funding Customer A's EUR Wallet with 3500.50 EUR
The process is similar to funding the USD wallet. Here’s the key part of the request body.
The principles explained for the USD transaction apply here as well. Always ensure you’re using the correct precision and currency for each transaction.
4. Moving money out of wallets
Once the ledgers are created and balances/wallets have been set up for your customers, and the wallets have been funded, you can record transactions that move money out of the wallets.
See also:
Transferring $70.32 from Customer A's USD Wallet
Field | Description |
---|---|
amount | The amount to be transferred, in this case 70.32 USD. |
precision | As before, we use 100 to represent cents. |
source | This time, we’re transferring from the customer’s wallet, so we use the balance_id of their USD wallet. |
destination | We’re transferring to the outside world, so we use “@World”. |
allow_overdraft | This is set to false because we don’t want to allow the customer’s balance to go negative. |
Always check the balance after a transaction to ensure it’s updated correctly. Here’s how you might do that.
Balance after the transaction:
Note that the balance has decreased by 7031 cents (70.31 USD), which matches our transaction amount.
Transferring 1470.49 EUR from Customer A's EUR Wallet
The process is similar to the USD transfer. Here’s the key part of the request body:
Always ensure you’re using the correct balance_id
for the source wallet and the correct currency for each transaction.
5. Moving money between multicurrency wallets
Blnk supports moving money between balances of different currencies. This feature is crucial for applications dealing with multiple currencies.
See also:
Converting $100 USD to EUR (at a rate of 1 USD = 0.92 EUR)
Field | Description |
---|---|
amount | The amount to be converted, in this case 100 USD. |
precision | As before, we use 100 to represent cents. |
source | The balance_id of the USD wallet. |
destination | The balance_id of the EUR wallet. |
rate | The conversion rate from USD to EUR. In this case, 1 USD = 0.92 EUR. |
meta_data | It’s crucial to store information about the rate used and its source for auditing purposes. |
Always store the conversion rate and its source in the meta_data. This is crucial for auditing and reconciliation.
After this transaction, you should check both the USD and EUR balances
USD Balance after the transaction:
EUR Balance after the transaction:
Note that the USD balance has decreased by 10000 cents (100 USD), and the EUR balance has increased by 9200 cents (92 EUR), which matches our conversion rate.
See also
Managing side effects with Inflight
A deep-dive guide into how to implement Inflight in your application.
Best practices
- Immediate Action: Process refunds as soon as you receive notification of a failed verification to ensure good customer experience.
- Detailed Logging: Always include detailed information in the
meta_data
field. This aids in troubleshooting and auditing. - Balance Verification: Always verify the balance after processing a refund to ensure the transaction was successful.
- Error Handling: Implement robust error handling in your refund process. If a refund fails, you may need to retry or escalate to manual intervention.
- Customer Communication: Implement a system to notify customers about the failed transaction and subsequent refund.
- Reconciliation: Regularly reconcile your internal records with Blnk’s transaction and refund logs to ensure accuracy.
- Verify Webhook Authenticity: In a production environment, implement a mechanism to verify that the webhook is genuinely from your payment provider. This often involves checking a signature or secret key.
- Idempotency: Ensure your webhook handler is idempotent. Providers may send the same webhook multiple times, so your system should handle duplicate notifications gracefully.
- Asynchronous Processing: For high-volume systems, consider processing webhooks asynchronously. You can acknowledge receipt immediately and process the webhook contents in a background job.
- Monitoring: Set up monitoring and alerting for your webhook endpoint. This can help you quickly identify and respond to any issues in the payment verification and refund process.
Need help?
We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool.
To ask questions or discuss issues, please join our Discord community.
Was this page helpful?