Guest post by Pavlo Sidelov, Co-Founder of Cloud Wallet.
Cloud Wallet is an API that covers asset and inventory accounting and enables closed-loop payments between connected devices. Having our endpoints freshly published on RapidAPI, with this post I want to have RapidAPI blog readers take a look at Cloud Wallet. I have chosen inventory accounting for a coffee vending machine to present some examples.
Imagine you run a fleet of 1,000 coffee machines and you need to address the challenge of restocking in a timely fashion. To avoid lost sales and excessive delivery costs, we will take care of inventory accounting by linking each vending machine to a set of wallet that will record inventory balances and changes.
With Cloud Wallet API it is possible to assign wallets to users or connected devices, record available amounts of something valuable, as well as all incoming and outgoing movements of value. Let’s examine how this functionality will help us in securing timely restocking for our imaginary vending machines and potentially add operational efficiency to the vending business at large.
How to Build a Smarter Coffee Machine Using Cloud Wallet on RapidAPI
Step 1: To start anything, you need to create a project. Projects store wallets and process transactions that belong to one application.
Call the “Create project” endpoint, providing the project name and the information about the first asset type for accounting cash income (named “US Dollar”, breakable into 100 fractions, cents).
Request:
POST /projects { "name": "Coffee machine", "assetCode": "USD", "assetName": "US Dollar", "fractions": "100" }
Response:
{ "id": "project_1", "name": "Coffee machine", "assets": [{ "id": "a_cash", "name": "US Dollar", "code": "USD", "fractions": 100, "used": false }] }
Step 2: Speaking of asset types, they are like units of measurement for value. Transfer operations are possible only between wallets that hold value of the same asset type. Each asset type has its own resolution defined by a number of fractions that each unit can be broken into.
Call “List assets” to find a project’s available assets or use “Create an asset” to add more asset types. For the machine, you may need additional types for water and milk (named “Water, liter” and “Milk, liter”, both breakable into 1000 milliliters), cups (“Cup”, 1 piece) and coffee shots (“Coffee Shot”, 1 piece).
Request:
POST /projects/project_1/assets { "name": "Water, liter", "code": "WTR", "fractions": 1000 }
Response:
{ "id": "a_water", "name": "Water, liter", "code": "WTR", "fractions": 1000, "used": false }
Step 3: Next, let’s create a customer record. In our API, customers are wallet owners. You can map a customer entity to your users, devices, vehicles, or appliances. In other words, a “customer” is anything that you want to link wallets to.
In our case, each coffee machine will require its own customer record. Call the “Create a customer” endpoint (you must provide an identifier at the very least) and the record will be created. Since you have to choose customer identifiers, please note that they must be unique within one project.
Request:
POST /projects/project_1/customers { "customerId": "CM1", "name": "Coffee machine 1" "description": "My first coffee machine" }
Response:
{ "id": "CM1", "name": "Coffee machine 1", "description": "My first coffee machine", "status": "Active" }
Step 4: Our vending coffee machine has four ingredients: coffee beans, water, milk, and cups. When we brew a cup of coffee, some of those ingredients are consumed in exchange for a cash payment. By creating wallets for each type of ingredient and a cash collector, we will monitor expenses and income.
Wallets are opened with the “Open a wallet” endpoint. You provide the wallet owner’s (“customer”) identifier, asset type identifier, and a wallet name.
Request:
POST /projects/project_1/wallets { "customerId":"CM1", "assetId": "a_water", "name": "Water" }
Response:
{ "id": "w_cm1_water", "projectId": "project_1", "customerId": "CM1", "assetId": "a_water", "balance": "0", "status": "Open", "name": "Water" }
Step 5: Our wallets act just like accounts. They know the currently available amount of something valuable. Transactions functionality allows you to create, destroy, and move value between wallets.
To put the machine into operation, we must preload it with ingredients. That will be your first transaction. It comprises a set of “Issue” operations that will create value in wallets. Below is an example of calling the endpoint “Run a new transaction” with one Issue operation to a milk wallet.
The reference ID here and later is a unique identifier that you must provide when creating a transaction. It is used to prevent transaction duplication in the case of connection errors.
Request:
POST /projects/project_1/transactions { "operations": [ { "type": "Issue", "creditWalletId": "w_cm1_water", "amount": "2.000", "assetId": "a_water", "description": "Initial loading" }, { "type": "Issue", "creditWalletId": "w_cm1_milk", "amount": "1.000", "assetId": "a_milk", "description": "Initial loading" }, { "type": "Issue", "creditWalletId": "w_cm1_coffee", "amount": "25", "assetId": "a_coffee_shots", "description": "Initial loading" }, { "type": "Issue", "creditWalletId": "w_cm1_cups", "amount": "10", "assetId": "a_coffee_cups", "description": "Initial loading"}], "description": "Initial loading", "referenceId": "initial_1" }
Response:
{ "result": "Completed", "transaction": { "id": "tx_1", "referenceId": "initial_1", "status": "Completed", "operations": [ { "id": "tx_1_1", "type": "Issue", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_water", "amount": "2", "assetId": "a_water", "description": "Initial loading" }, { "id": "tx_1_2", "type": "Issue", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_milk", "amount": "1", "assetId": "a_milk", "description": "Initial loading" }, { "id": "tx_1_3", "type": "Issue", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_coffee", "amount": "25", "assetId": "a_coffee_shots", "description": "Initial loading" }, { "id": "tx_1_4", "type": "Issue", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_cups", "amount": "10", "assetId": "a_coffee_cups", "description": "Initial loading" }], "description": "Initial loading", "timeInitiated": "2019-08-26T17:38:21.085Z", "timeFinalized": "2019-08-26T17:38:21.119Z" } }
Note: It is worth mentioning that some parts of our system are eventually consistent. There will be a certain delay between the completion of a transaction and seeing its results in listings from other endpoints. Wallets themselves have strong consistency, so do not worry about your balances.
Step 6: Each sale will also be modeled as a transaction.
Drink types that we sell have their prices and require different amounts of ingredients to be produced. For example, a black coffee won’t consume milk, but a cappuccino will require a specific amount of it.
The transaction for a sale will consist of two groups of operations: consuming resources and accepting payment. From our point of view, consumption is a destruction of value. Redeem operation does exactly this by deducting from a balance. Accepting payment creates value inside our system. An issue operation will add this value to the cash wallet. So, a transaction from purchasing any drink will show that balances of required ingredients have been reduced and the cash balance has been increased.
In the example below, we purchased a cappuccino for $2.80. One cup, two coffee shots, some milk and water were consumed, and the cash balance added the payment amount.
Request:
POST /projects/project_1/transactions { "operations": [ { "type": "Redeem", "creditWalletId": "w_cm1_cups", "amount": "1", "assetId": "a_coffee_cups", "description": "Preparing cappuccino"}, { "type": "Redeem", "creditWalletId": "w_cm1_water", "amount": "0.060", "assetId": "a_water", "description": "Preparing cappuccino" }, { "type": "Redeem", "creditWalletId": "w_cm1_coffee", "amount": "2", "assetId": "a_coffee_shots", "description": "Preparing cappuccino" }, { "type": "Redeem", "creditWalletId": "w_cm1_milk", "amount": "0.120", "assetId": "a_milk", "description": "Preparing cappuccino" }, { "type": "Issue", "creditWalletId": "w_cm1_cash", "amount": "2.80", "assetId": "a_cash", "description": "Payment for cappuccino"}], "description": "Cappuccino sale", "referenceId": "sale_1" }
Response:
{ "result": "Completed", "transaction": { "id": "tx_2", "referenceId": " sale_1", "status": "Completed", "operations": [ { "id": "tx_2_1", "type": "Redeem", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_cups", "amount": "1", "assetId": "a_coffee_cups", "description": "Preparing cappuccino" }, { "id": "tx_2_2", "type": "Redeem", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_water", "amount": "0.06", "assetId": "a_water", "description": "Preparing cappuccino" }, { "id": "tx_2_3", "type": "Redeem", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_coffee", "amount": "2", "assetId": "a_coffee_shots", "description": "Preparing cappuccino" }, { "id": "tx_2_4", "type": "Redeem", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_milk", "amount": "0.12", "assetId": "a_milk", "description": "Preparing cappuccino" }, { "id": "tx_2_5", "type": "Issue", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_cash", "amount": "2.80", "assetId": "a_cash", "description": "Payment for cappuccino" }], "description": "Cappuccino sale", "timeInitiated": "2019-08-26T17:51:20.575Z", "timeFinalized": "2019-08-26T17:51:20.624Z" } }
Step 7: Finally, we have to model the restocking process. This time let’s imagine that we pay our supplier not in cash, but to their own cash wallet in our system. A transfer operation moves value between wallets. When the machine is restocked, the supplier’s wallet will be credited for the value of added goods from the machine’s cash wallet. The restock transaction will also create value in the inventory wallets.
This is an example of restocking the milk wallet.
Request:
POST /projects/project_1/transactions { "operations": [ { "type": "Issue", "creditWalletId": "w_cm1_water", "amount": "0.120", "assetId": "a_water", "description": "Restock" }, { "type": "Issue", "creditWalletId": "w_cm1_milk", "amount": "0.240", "assetId": "a_milk", "description": "Restock" }, { "type": "Issue", "creditWalletId": "w_cm1_coffee", "amount": "4", "assetId": "a_coffee_shots", "description": "Restock" }, { "type": "Issue", "creditWalletId": "w_cm1_cups", "amount": "2", "assetId": "a_coffee_cups", "description": "Restock"}, { "type": "Transfer", "debitWalletId": "w_cm1_cash", "creditWalletId": "w_suplier_cash", "amount": "1.08", "assetId": "a_cash", "description": "Payment to supplier"}], "description": "Restock at 2019-08-26T18:00:00Z", "referenceId": "restock_1" }
Response:
{ "result": "Completed", "transaction": { "id": "tx_10", "referenceId": "restock_1", "status": "Completed", "operations": [ { "id": "tx_10_1", "type": "Issue", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_water", "amount": "0.12", "assetId": "a_water", "description": "Restock" }, { "id": "tx_10_2", "type": "Issue", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_milk", "amount": "0.24", "assetId": "a_milk", "description": "Restock" }, { "id": "tx_10_3", "type": "Issue", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_coffee", "amount": "4", "assetId": "a_coffee_shots", "description": "Restock" }, { "id": "tx_10_4", "type": "Issue", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "creditWalletId": "w_cm1_cups", "amount": "2", "assetId": "a_coffee_cups", "description": "Restock" }, { "id": "tx_10_5", "type": "Transfer", "status": "Completed", "debitResult": "Success", "debitReason": "Done", "creditResult": "Success", "creditReason": "Done", "debitWalletId": "w_cm1_cash", "creditWalletId": "w_supplier_cash", "amount": "1.08", "assetId": "a_cash", "description": "Payment to supplier" }], "description": " Restock at 2019-08-26T18:00:00Z", "timeInitiated": "2019-08-26T18:00:02.454Z", "timeFinalized": "2019-08-26T18:00:02.501Z" } }
Each complex transaction will follow one basic rule: the whole transaction will be completed only if each operation in it can be performed. Should any single operation be impossible, due to the low balance of one of the wallets, for instance, the whole transaction will fail without affecting any wallet.
Cloud Wallet Summary
That’s it! With one quick and easy integration with Cloud Wallet on RapidAPI, you can implement the inventory accounting for your coffee machine or for any other business domain.
In fact, this is only one of many possible use cases in which you can create additional value for your business by leveraging Cloud Wallet API. Apart from asset and inventory accounting, wallets can serve for loyalty programs, bonus points systems, internal P2P transfers, and Machine-2-Machine payments.
Try out Cloud Wallet API on RapidAPI now and be sure to tell us how Cloud Wallet helped your business!
Leave a Reply