The transaction ID lets you monitor transaction status.
The transaction flow inherently operates asynchronously, involving the approval of the transaction, its subsequent signing, and the waiting period for the blockchain to mine the transaction.
To allow tracking of the transaction flow, Fordefi provides a unique transaction ID upon submission. This transaction ID serves as Fordefi's internal representation of the transaction and will enable you to monitor the status of the transaction. Use this ID to stay informed about the progress and current state of the transaction. Once the transaction has been created, it progresses according to the Transaction Lifecycle.
There are several approaches to monitoring transaction states:
- Signing up for webhook notifications and monitoring the received webhooks.
- Polling via the REST API
We strongly recommend using webhook notifications to monitor transactions, as they offer several advantages over the polling mechanism:
- Minimal delays: With polling, you become aware of the transaction’s progress only in the next poll request, leading to potential staleness in your data up to the polling interval. In contrast, Webhooks provide real-time notifications, instantly informing you the moment a transaction makes progress.
- No rate limit: The Fordefi REST API imposes rate limits, restricting the number of queries that can be sent by REST clients within a specific time interval. However, webhooks are not subject to rate limits since they are initiated by the Fordefi system itself, not by the client.
- Efficient computation usage: Polling transactions involves repeatedly querying the transaction status, resulting in many unchanged responses. This can lead to redundant work for the client as no changes have occurred yet. Webhooks, on the other hand, notify you only when a transaction has made progress, minimizing unnecessary computation and optimizing efficiency.
Monitor transactions using webhooks
Webhook messages sent by Fordefi to your service use JSON structures, including the specification of the incoming POST requests format under the Callbacks section. See the Fordefi API reference for more information.
Webhooks send updates of transactions whenever their state is changed, including updates of incoming transactions (transactions whose origin is not managed by the organization).
Using the following HTTP Server, you can start monitoring transactions:
import http
import http.server
import json
import logging
PORT = 8080
logger = logging.getLogger(__name__)
class WebhooksListener(http.server.BaseHTTPRequestHandler):
def do_POST(self):
logger.info("Received POST request")
content_length = int(self.headers["Content-Length"])
body = self.rfile.read(content_length)
event = json.loads(body)
event_type = event["event_type"]
logger.info(f"Received a new event of type: {event_type=}")
if event_type == "transaction_state_update":
self._on_transaction_update(event["event"])
self.send_response(http.HTTPStatus.OK, "OK")
self.end_headers()
def _on_transaction_update(self, transaction_event) -> None:
transaction_id = transaction_event["transaction_id"]
state = transaction_event["state"]
logger.info(f"Received a transaction update: {transaction_id=} changed state to {state=}")
def log_message(format, *args):
pass
def setup_logging() -> None:
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(asctime)s:%(name)s:%(levelname)s:%(message)s"))
logger.setLevel(logging.INFO)
logger.addHandler(handler)
def main() -> None:
setup_logging()
logger.info(f"Listening on port {PORT}...")
http.server.HTTPServer(("0.0.0.0", PORT), WebhooksListener).serve_forever()
if __name__ == "__main__":
main()
Run the server:
$ python monitor.py
This output indicates that the server is up and running, listening to port 8080:
2023-07-18 12:22:35,417:__main__:INFO:Listening on port 8080...
Expose your server easily using ngrok. Copy the URL that is referenced in the Forwarding
field (for example, https://84c5df439d74.ngrok-free.dev
in the sample output below) and configure the webhook using the instructions in Configure a Webhook.
$ ngrok http 8080
ngrok
(Ctrl+C to quit)
Session Status online
Account inconshreveable (Plan: Free)
Version 3.0.0
Region United States (us)
Latency 78ms
Web Interface http://127.0.0.1:4040
Forwarding https://84c5df439d74.ngrok-free.dev -> http://localhost:8080
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
Following the instructions in Validate a webhook to check your webhook setup.
You should see an indication of success in the web console:
And in your console, the server should output:
2023-07-18 12:29:37,721:__main__:INFO:Received POST request
2023-07-18 12:29:37,721:__main__:INFO:Received a new event of type: event_type='transaction_state_update'
2023-07-18 12:29:37,721:__main__:INFO:Received a transaction update: transaction_id='6b2a6f2b-2c2f-4c6b-9e1b-6d8f6a9b9c9d' changed state to state='pushed_to_blockchain'
Monitor transactions using REST API
If you prefer a synchronous API for monitoring the transactions, you can use the following example, which will poll until the transaction reaches the Completed state.
import argparse
import logging
import time
import uuid
import requests
PORT = 8080
logger = logging.getLogger(__name__)
def setup_logging() -> None:
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(asctime)s:%(name)s:%(levelname)s:%(message)s"))
logger.setLevel(logging.INFO)
logger.addHandler(handler)
def monitor_transaction(
access_token: str,
transaction_id: uuid.UUID,
polling_interval: int,
) -> None:
setup_logging()
logger.info(f"Monitoring transaction: {transaction_id=}")
current_state = None
while True:
transaction = requests.get(
url=f"https://api.fordefi.com/api/v1/transactions/{transaction_id}",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
},
).json()
new_state = transaction["state"]
if new_state != current_state:
logger.info(f"Transaction {transaction_id=} changed state from {current_state=} to {new_state=}")
current_state = new_state
if new_state == "completed":
break
else:
logger.info(f"Transaction {transaction_id=} is still in state {current_state=}")
logger.info(f"Sleeping for {polling_interval=} seconds")
time.sleep(polling_interval)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--transaction-id", type=uuid.UUID, required=True, help="Transaction ID to monitor")
parser.add_argument("--access-token", type=str, required=True, help="Access token for Fordefi API")
parser.add_argument("--polling-interval", type=int, default=5, help="Polling interval in seconds")
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
monitor_transaction(
access_token=args.access_token,
transaction_id=args.transaction_id,
polling_interval=args.polling_interval,
)
Then monitor the transaction by running:
$ python monitor.py --transaction-id bd28ab46-5874-4470-8b98-b1531165e347 --access-token <YOUR-FORDEFI-REST-API-ACCESS-TOKEN>
Possible output:
2023-07-18 16:00:42,949:__main__:INFO:Monitoring transaction: transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347')
2023-07-18 16:00:43,690:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') changed state from current_state=None to new_state='approved'
2023-07-18 16:00:43,690:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:00:49,386:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='approved'
2023-07-18 16:00:49,386:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:00:55,102:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') changed state from current_state='approved' to new_state='pushed_to_blockchain'
2023-07-18 16:00:55,102:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:01:00,769:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='pushed_to_blockchain'
2023-07-18 16:01:00,769:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:01:06,459:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='pushed_to_blockchain'
2023-07-18 16:01:06,459:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:01:12,168:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='pushed_to_blockchain'
2023-07-18 16:01:12,168:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:01:17,901:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='pushed_to_blockchain'
2023-07-18 16:01:17,901:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:01:23,611:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='pushed_to_blockchain'
2023-07-18 16:01:23,611:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:01:29,362:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='pushed_to_blockchain'
2023-07-18 16:01:29,362:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:01:35,083:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='pushed_to_blockchain'
2023-07-18 16:01:35,083:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:01:40,755:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') changed state from current_state='pushed_to_blockchain' to new_state='mined'
2023-07-18 16:01:40,755:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:01:46,460:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:01:46,460:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:01:52,154:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:01:52,155:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:01:57,849:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:01:57,850:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:02:03,541:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:02:03,541:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:02:09,220:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:02:09,220:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:02:14,916:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:02:14,916:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:02:20,666:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:02:20,666:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:02:26,357:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:02:26,357:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:02:32,059:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:02:32,059:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:02:37,768:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:02:37,768:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:02:43,462:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:02:43,462:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:02:49,159:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:02:49,159:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:02:54,827:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') is still in state current_state='mined'
2023-07-18 16:02:54,827:__main__:INFO:Sleeping for polling_interval=5 seconds
2023-07-18 16:03:00,738:__main__:INFO:Transaction transaction_id=UUID('bd28ab46-5874-4470-8b98-b1531165e347') changed state from current_state='mined' to new_state='completed'