Listening for ada payments using cardano-cli
#
Overviewnote
This guide assumes that you have basic understanding of cardano-cli
, how to use it and that you have installed it into your system. Otherwise we recommend reading Installing cardano-node, Running cardano-node and Exploring Cardano Wallets guides first.
This guide also assumes that you have cardano-node
running in the background and connected to the testnet
network.
#
Use caseThere are many possible reasons why you would want to have the functionality of listening for ada
payments, but a very obvious use case would be for something like an online shop or a payment gateway that uses ada
tokens as the currency.
#
Technical flowTo understand how something like this could work in a technical point of view, let's take a look at the following diagram:
So let's imagine a very basic scenario where a customer is browsing an online shop. Once the user has choosen and added all the items into the shopping cart. The next step would then be to checkout and pay for the items, Of course we will be using Cardano for that!
The front-end application would then request for a wallet address from the backend service and render a QR code to the customer to be scanned via a Cardano wallet. The backend service would then know that it has to query the wallet address using cardano-cli
with a certain time interval to confirm and alert the front-end application that the payment has completed succesfully.
In the meantime the transaction is then being processed and settled within the Cardano network. We can see in the diagram above that both parties are ultimately connected to the network via the cardano-node
software component.
#
Time to codeNow let's get our hands dirty and see how we can implement something like this in actual code.
note
In this section, We will use the path /home/user/receive-ada-sample
to store all the related files as an example, please replace it with the directory you have choosen to store the files.
All the code examples in this article assumes that you will save all the source-code-files under the root of this directory.
#
Generate keys and request tAdaFirst, lets create a directory to store our sample project:
mkdir -p /home/user/receive-ada-sample/keys
Next, we generate our payment key-pair using cardano-cli
:
cardano-cli address key-gen \--verification-key-file /home/user/receive-ada-sample/keys/payment.vkey \--signing-key-file /home/user/receive-ada-sample/keys/payment.skey
Since we now have our payment key-pair, the next step would be to generate a wallet address for the testnet
network like so:
cardano-cli address build \--payment-verification-key-file /home/user/receive-ada-sample/keys/payment.vkey \--out-file /home/user/receive-ada-sample/keys/payment.addr \--testnet-magic 1097911063
Your directory structure should now look like this:
/home/user/receive-ada-sample/receive-ada-sampleโโโ keys โโโ payment.addr โโโ payment.skey โโโ payment.vkey
Now using your programming language of choice we create our first few lines of code!
#
Initial variablesFirst we will set the initial variables that we will be using as explained below:
- JavaScript
- Typescript
- Python
- C#
import * as fs from 'fs';// Please add this dependency using npm install node-cmdimport cmd from 'node-cmd';
// Path to the cardano-cli binary or use the global oneconst CARDANO_CLI_PATH = "cardano-cli";// The `testnet` identifier numberconst CARDANO_NETWORK_MAGIC = 1097911063;// The directory where we store our payment keys// assuming our current directory context is /home/user/receive-ada-sampleconst CARDANO_KEYS_DIR = "keys";// The total payment we expect in lovelace unitconst TOTAL_EXPECTED_LOVELACE = 1000000;
import * as fs from 'fs';// Please add this dependency using npm install node-cmd but there is no @type definition for itconst cmd: any = require('node-cmd');
// Path to the cardano-cli binary or use the global oneconst CARDANO_CLI_PATH: string = "cardano-cli";// The `testnet` identifier numberconst CARDANO_NETWORK_MAGIC: number = 1097911063;// The directory where we store our payment keys// assuming our current directory context is /home/user/receive-ada-sample/receive-ada-sampleconst CARDANO_KEYS_DIR: string = "keys";// The total payment we expect in lovelace unitconst TOTAL_EXPECTED_LOVELACE: number = 1000000;
import osimport subprocess
# Path to the cardano-cli binary or use the global oneCARDANO_CLI_PATH = "cardano-cli"# The `testnet` identifier numberCARDANO_NETWORK_MAGIC = 1097911063# The directory where we store our payment keys# assuming our current directory context is /home/user/receive-ada-sampleCARDANO_KEYS_DIR = "keys"# The total payment we expect in lovelace unitTOTAL_EXPECTED_LOVELACE = 1000000
using System.Linq;using SimpleExec; // `dotnet add package SimpleExec --version 7.0.0`
// Path to the cardano-cli binary or use the global oneconst string CARDANO_CLI_PATH = "cardano-cli";// The `testnet` identifier numberconst int CARDANO_NETWORK_MAGIC = 1097911063;// The directory where we store our payment keys// assuming our current directory context is /home/user/receive-ada-sampleconst string CARDANO_KEYS_DIR = "keys";// The total payment we expect in lovelace unitconst long TOTAL_EXPECTED_LOVELACE = 1000000;
#
Read wallet address valueNext, we get the string value of the wallet address from the payment.addr
file that we generated awhile ago. Add the following lines to your code:
- JavaScript
- Typescript
- Python
- C#
// Read wallet address value from payment.addr fileconst walletAddress = fs.readFileSync(`${CARDANO_KEYS_DIR}/payment.addr`).toString();
// Read wallet address string value from payment.addr fileconst walletAddress: string = fs.readFileSync(`${CARDANO_KEYS_DIR}/payment.addr`).toString();
# Read wallet address value from payment.addr filewith open(os.path.join(CARDANO_KEYS_DIR, "payment.addr"), 'r') as file: walletAddress = file.read()
// Read wallet address value from payment.addr filevar walletAddress = await System.IO.File.ReadAllTextAsync($"{CARDANO_KEYS_DIR}/payment1.addr");
#
Query UTxOThen we execute cardano-cli
programatically and telling it to query the UTxO for the wallet address that we have generated with our keys and save the stdout
result to our rawUtxoTable
variable.
- JavaScript
- Typescript
- Python
- C#
// We use the node-cmd npm library to execute shell commands and read the output dataconst rawUtxoTable = cmd.runSync([ CARDANO_CLI_PATH, "query", "utxo", "--testnet-magic", CARDANO_NETWORK_MAGIC, "--address", walletAddress].join(" "));
// We use the node-cmd npm library to execute shell commands and read the output dataconst rawUtxoTable: any = cmd.runSync([ CARDANO_CLI_PATH, "query", "utxo", "--testnet-magic", CARDANO_NETWORK_MAGIC, "--address", walletAddress].join(" "));
# We tell python to execute cardano-cli shell command to query the UTXO and read the output datarawUtxoTable = subprocess.check_output([ CARDANO_CLI_PATH, 'query', 'utxo', '--testnet-magic', str(CARDANO_NETWORK_MAGIC), '--address', walletAddress])
// We use the SimpleExec dotnet library to execute shell commands and read the output datavar rawUtxoTable = await Command.ReadAsync(CARDANO_CLI_PATH, string.Join(" ", "query", "utxo", "--testnet-magic", CARDANO_NETWORK_MAGIC, "--address", walletAddress), noEcho: true);
#
Process UTxO tableOnce we have access to the UTXO table string, we will then parse it and compute the total lovelace that the wallet currently has.
- JavaScript
- Typescript
- Python
- C#
// Calculate total lovelace of the UTXO(s) inside the wallet addressconst utxoTableRows = rawUtxoTable.data.trim().split('\n');let totalLovelaceRecv = 0;let isPaymentComplete = false;
for (let x = 2; x < utxoTableRows.length; x++) { const cells = utxoTableRows[x].split(" ").filter(i => i); totalLovelaceRecv += parseInt(cells[2]);}
// Calculate total lovelace of the UTXO(s) inside the wallet addressconst utxoTableRows: string[] = rawUtxoTable.data.trim().split('\n');let totalLovelaceRecv: number = 0;let isPaymentComplete: boolean = false;
for (let x = 2; x < utxoTableRows.length; x++) { const cells = utxoTableRows[x].split(" ").filter((i: string) => i); totalLovelaceRecv += parseInt(cells[2]);}
# Calculate total lovelace of the UTXO(s) inside the wallet addressutxoTableRows = rawUtxoTable.strip().splitlines()totalLovelaceRecv = 0isPaymentComplete = False
for x in range(2, len(utxoTableRows)): cells = utxoTableRows[x].split() totalLovelaceRecv += int(cells[2])
// Calculate total lovelace of the UTXO(s) inside the wallet addressvar utxoTableRows = rawUtxoTable.Trim().Split("\n");var totalLovelaceRecv = 0L;var isPaymentComplete = false;
foreach(var row in utxoTableRows.Skip(2)){ var cells = row.Split(" ").Where(c => c.Trim() != string.Empty); totalLovelaceRecv += long.Parse(cells.ElementAt(2));}
#
Determine if payment is succesfulOnce we have the total lovelace amount, we will then determine using our code if a specific payment is a success, ultimately sending or shipping the item if it is indeed succesful. In our example, we expect that the payment is equal to 1,000,000 lovelace
that we defined in our TOTAL_EXPECTED_LOVELACE
constant variable.
- JavaScript
- Typescript
- Python
- C#
// Determine if the total lovelace received is more than or equal to// the total expected lovelace and displaying the results.isPaymentComplete = totalLovelaceRecv >= TOTAL_EXPECTED_LOVELACE;
console.log(`Total Received: ${totalLovelaceRecv} LOVELACE`);console.log(`Expected Payment: ${TOTAL_EXPECTED_LOVELACE} LOVELACE`);console.log(`Payment Complete: ${(isPaymentComplete ? "โ
" : "โ")}`);
// Determine if the total lovelace received is more than or equal to// the total expected lovelace and displaying the results.isPaymentComplete = totalLovelaceRecv >= TOTAL_EXPECTED_LOVELACE;
console.log(`Total Received: ${totalLovelaceRecv} LOVELACE`);console.log(`Expected Payment: ${TOTAL_EXPECTED_LOVELACE} LOVELACE`);console.log(`Payment Complete: ${(isPaymentComplete ? "โ
" : "โ")}`);
# Determine if the total lovelace received is more than or equal to# the total expected lovelace and displaying the results.isPaymentComplete = totalLovelaceRecv >= TOTAL_EXPECTED_LOVELACE
print("Total Received: %s LOVELACE" % totalLovelaceRecv)print("Expected Payment: %s LOVELACE" % TOTAL_EXPECTED_LOVELACE)print("Payment Complete: %s" % {True: "โ
", False: "โ"} [isPaymentComplete])
// Determine if the total lovelace received is more than or equal to// the total expected lovelace and displaying the results.isPaymentComplete = totalLovelaceRecv >= TOTAL_EXPECTED_LOVELACE;
System.Console.WriteLine($"Total Received: {totalLovelaceRecv} LOVELACE");System.Console.WriteLine($"Expected Payment: {TOTAL_EXPECTED_LOVELACE} LOVELACE");System.Console.WriteLine($"Payment Complete: {(isPaymentComplete ? "โ
":"โ")}");
#
Running and testingOur final code should look something like this:
- JavaScript
- Typescript
- Python
- C#
import * as fs from 'fs';// Please add this dependency using npm install node-cmdimport cmd from 'node-cmd';
// Path to the cardano-cli binary or use the global oneconst CARDANO_CLI_PATH = "cardano-cli";// The `testnet` identifier numberconst CARDANO_NETWORK_MAGIC = 1097911063;// The directory where we store our payment keys// assuming our current directory context is /home/user/receive-ada-sample/receive-ada-sampleconst CARDANO_KEYS_DIR = "keys";// The imaginary total payment we expect in lovelace unitconst TOTAL_EXPECTED_LOVELACE = 1000000;
// Read wallet address string value from payment.addr fileconst walletAddress = fs.readFileSync(`${CARDANO_KEYS_DIR}/payment.addr`).toString();
// We use the node-cmd npm library to execute shell commands and read the output dataconst rawUtxoTable = cmd.runSync([ CARDANO_CLI_PATH, "query", "utxo", "--testnet-magic", CARDANO_NETWORK_MAGIC, "--address", walletAddress].join(" "));
// Calculate total lovelace of the UTXO(s) inside the wallet addressconst utxoTableRows = rawUtxoTable.data.trim().split('\n');let totalLovelaceRecv = 0;let isPaymentComplete = false;
for(let x = 2; x < utxoTableRows.length; x++) { const cells = utxoTableRows[x].split(" ").filter(i => i); totalLovelaceRecv += parseInt(cells[2]);}
// Determine if the total lovelace received is more than or equal to// the total expected lovelace and displaying the results.isPaymentComplete = totalLovelaceRecv >= TOTAL_EXPECTED_LOVELACE;
console.log(`Total Received: ${totalLovelaceRecv} LOVELACE`);console.log(`Expected Payment: ${TOTAL_EXPECTED_LOVELACE} LOVELACE`);console.log(`Payment Complete: ${(isPaymentComplete ? "โ
" : "โ")}`);
import * as fs from 'fs';// Please add this dependency using npm install node-cmd but there is no @type definition for itconst cmd: any = require('node-cmd');
// Path to the cardano-cli binary or use the global oneconst CARDANO_CLI_PATH: string = "cardano-cli";// The `testnet` identifier numberconst CARDANO_NETWORK_MAGIC: number = 1097911063;// The directory where we store our payment keys// assuming our current directory context is /home/user/receive-ada-sample/receive-ada-sampleconst CARDANO_KEYS_DIR: string = "keys";// The imaginary total payment we expect in lovelace unitconst TOTAL_EXPECTED_LOVELACE: number = 1000000;
// Read wallet address string value from payment.addr fileconst walletAddress: string = fs.readFileSync(`${CARDANO_KEYS_DIR}/payment.addr`).toString();
// We use the node-cmd npm library to execute shell commands and read the output dataconst rawUtxoTable: any = cmd.runSync([ CARDANO_CLI_PATH, "query", "utxo", "--testnet-magic", CARDANO_NETWORK_MAGIC, "--address", walletAddress].join(" "));
// Calculate total lovelace of the UTXO(s) inside the wallet addressconst utxoTableRows: string[] = rawUtxoTable.data.trim().split('\n');let totalLovelaceRecv: number = 0;let isPaymentComplete: boolean = false;
for (let x = 2; x < utxoTableRows.length; x++) { const cells = utxoTableRows[x].split(" ").filter((i: string) => i); totalLovelaceRecv += parseInt(cells[2]);}
// Determine if the total lovelace received is more than or equal to// the total expected lovelace and displaying the results.isPaymentComplete = totalLovelaceRecv >= TOTAL_EXPECTED_LOVELACE;
console.log(`Total Received: ${totalLovelaceRecv} LOVELACE`);console.log(`Expected Payment: ${TOTAL_EXPECTED_LOVELACE} LOVELACE`);console.log(`Payment Complete: ${(isPaymentComplete ? "โ
" : "โ")}`);
using System;using System.IO;using System.Linq;
// Install using command `dotnet add package SimpleExec --version 7.0.0`using SimpleExec;
// Path to the cardano-cli binary or use the global oneconst string CARDANO_CLI_PATH = "cardano-cli";// The `testnet` identifier numberconst int CARDANO_NETWORK_MAGIC = 1097911063;// The directory where we store our payment keys// assuming our current directory context is /home/user/receive-ada-sampleconst string CARDANO_KEYS_DIR = "keys";// The total payment we expect in lovelace unitconst long TOTAL_EXPECTED_LOVELACE = 1000000;
// Read wallet address string value from payment.addr filevar walletAddress = await File.ReadAllTextAsync(Path.Combine(CARDANO_KEYS_DIR, "payment.addr"));
// We use the SimpleExec library to execute cardano-cli shell command to query the wallet UTXO and read the output datavar rawUtxoTable = await Command.ReadAsync(CARDANO_CLI_PATH, string.Join(" ", "query", "utxo", "--testnet-magic", CARDANO_NETWORK_MAGIC, "--address", walletAddress), noEcho: true);
// Calculate total lovelace of the UTXO(s) inside the wallet addressvar utxoTableRows = rawUtxoTable.Trim().Split("\n");var totalLovelaceRecv = 0L;var isPaymentComplete = false;
foreach(var row in utxoTableRows.Skip(2)){ var cells = row.Split(" ").Where(c => c.Trim() != string.Empty); totalLovelaceRecv += long.Parse(cells.ElementAt(2));}
// Determine if the total lovelace received is more than or equal to// the total expected lovelace and displaying the results.isPaymentComplete = totalLovelaceRecv >= TOTAL_EXPECTED_LOVELACE;
Console.WriteLine($"Total Received: {totalLovelaceRecv} LOVELACE");Console.WriteLine($"Expected Payment: {TOTAL_EXPECTED_LOVELACE} LOVELACE");Console.WriteLine($"Payment Complete: {(isPaymentComplete ? "โ
":"โ")}");
import osimport subprocess
# Path to the cardano-cli binary or use the global oneCARDANO_CLI_PATH = "cardano-cli"# The `testnet` identifier numberCARDANO_NETWORK_MAGIC = 1097911063# The directory where we store our payment keys# assuming our current directory context is /home/user/receive-ada-sampleCARDANO_KEYS_DIR = "keys"# The total payment we expect in lovelace unitTOTAL_EXPECTED_LOVELACE = 1000000
# Read wallet address value from payment.addr filewith open(os.path.join(CARDANO_KEYS_DIR, "payment.addr"), 'r') as file: walletAddress = file.read()
# We tell python to execute cardano-cli shell command to query the UTXO and read the output datarawUtxoTable = subprocess.check_output([ CARDANO_CLI_PATH, 'query', 'utxo', '--testnet-magic', str(CARDANO_NETWORK_MAGIC), '--address', walletAddress])
# Calculate total lovelace of the UTXO(s) inside the wallet addressutxoTableRows = rawUtxoTable.strip().splitlines()totalLovelaceRecv = 0isPaymentComplete = False
for x in range(2, len(utxoTableRows)): cells = utxoTableRows[x].split() totalLovelaceRecv += int(cells[2])
# Determine if the total lovelace received is more than or equal to# the total expected lovelace and displaying the results.isPaymentComplete = totalLovelaceRecv >= TOTAL_EXPECTED_LOVELACE
print("Total Received: %s LOVELACE" % totalLovelaceRecv)print("Expected Payment: %s LOVELACE" % TOTAL_EXPECTED_LOVELACE)print("Payment Complete: %s" % {True: "โ
", False: "โ"} [isPaymentComplete])
Your project directory should look something like this:
- JavaScript
- Typescript
- Python
- C#
# Excluding node_modules directory
/home/user/receive-ada-sample/receive-ada-sampleโโโ checkPayment.jsโโโ keysโย ย โโโ payment.addrโย ย โโโ payment.skeyโย ย โโโ payment.vkeyโโโ package-lock.jsonโโโ package.json
1 directories, 6 files
# Excluding node_modules directory
/home/user/receive-ada-sample/receive-ada-sampleโโโ checkPayment.tsโโโ keysโย ย โโโ payment.addrโย ย โโโ payment.skeyโย ย โโโ payment.vkeyโโโ package-lock.jsonโโโ package.json
1 directories, 6 files
# Excluding bin and obj directories
/home/user/receive-ada-sample/receive-ada-sampleโโโ Program.csโโโ dotnet.csprojโโโ keysโย ย โโโ payment.addrโย ย โโโ payment.skeyโย ย โโโ payment.vkey
1 directories, 5 files
/home/user/receive-ada-sample/receive-ada-sampleโโโ checkPayment.pyโโโ keys โโโ payment.addr โโโ payment.skey โโโ payment.vkey
1 directory, 4 files
Now we are ready to test ๐, running the code should give us the following result:
- JavaScript
- Typescript
- Python
- C#
โฏ node checkPayment.jsTotal Received: 0 LOVELACEExpected Payment: 1000000 LOVELACEPayment Complete: โ
โฏ ts-node checkPayment.tsTotal Received: 0 LOVELACEExpected Payment: 1000000 LOVELACEPayment Complete: โ
โฏ dotnet runTotal Received: 0 LOVELACEExpected Payment: 1000000 LOVELACEPayment Complete: โ
โฏ python checkPayment.py Total Received: 0 LOVELACEExpected Payment: 1000000 LOVELACEPayment Complete: โ
The code is telling us that our current wallet has received a total of 0 lovelace
and it expected 1,000,000 lovelace
, therefore it concluded that the payment is not complete.
#
Complete the paymentWhat we can do to simulate a succesful payment is to send atleast 1,000,000 lovelace
into the wallet address that we have just generated for this project. We can get the wallet address by reading the contents of the payment.addr
file like so:
cat /home/user/receive-ada-sample/receive-ada-sample/keys/payment.addr
You should see the wallet address value:
addr_test1vpfkp665a6wn7nxvjql5vdn5g5a94tc22njf4lf98afk6tgnz5ge4
Now simply send atleast 1,000,000 lovelace
to this wallet address or request some test ada
funds from the Cardano Testnet Faucet. Once complete, we can now run the code again and we should see a succesful result this time.
- JavaScript
- Typescript
- Python
- C#
โฏ node checkPayment.jsTotal Received: 1000000000 LOVELACEExpected Payment: 1000000 LOVELACEPayment Complete: โ
โฏ ts-node checkPayment.tsTotal Received: 1000000000 LOVELACEExpected Payment: 1000000 LOVELACEPayment Complete: โ
โฏ dotnet runTotal Received: 1000000000 LOVELACEExpected Payment: 1000000 LOVELACEPayment Complete: โ
โฏ python checkPayment.py Total Received: 1000000000 LOVELACEExpected Payment: 1000000 LOVELACEPayment Complete: โ
note
It might take 20 seconds or more for the transaction to propagate within the network depending on the network health, so you will have to be patient.
Congratulations, you are now able to detect succesful Cardano payments programatically. This should help you bring integrations to your existing or new upcoming applications. ๐๐๐