Code Examples
Ready-to-use code snippets for the MOR Fiscal API
Jump to Example
Create a Fiscal Receipt
Issue a tax-compliant receipt via the MOR API. Supports Category A (15% VAT) and Category B (0% VAT) merchants.
from mor_sdk import MorClientfrom decimal import Decimal
client = MorClient(api_key="sk_live_your_api_key")
# Create a fiscal receipt (Category A merchant — 15% VAT)receipt = client.receipts.create( merchant_tin="0012345678", device_id="device-uuid-here", items=[ { "description": "Ethiopian Coffee (1kg)", "quantity": 2, "unit_price": "350.00", "vat_rate": 15, }, { "description": "Injera Basket", "quantity": 1, "unit_price": "45.00", "vat_rate": 15, }, ], payment_method="ETHQR", total="745.00",)
print(f"Fiscal Code: {receipt.fiscal_code}")print(f"Receipt ID: {receipt.id}")Register a Virtual Fiscal Device
Register and activate a new VFD for your merchant. Supports branch assignment and auto-selection for single-branch merchants.
from mor_sdk import MorClient
client = MorClient(api_key="sk_live_your_api_key")
# Register a new virtual devicedevice = client.devices.create( merchant_tin="0012345678", device_name="POS Terminal #1", device_type="virtual", signing_algorithm="ECDSA-P256", station_id="POS-01", # branch_id is auto-selected for single-branch merchants)
print(f"Device ID: {device.id}")print(f"Serial: {device.serial_number}")print(f"Status: {device.status}") # pending_activation
# Activate the deviceactivated = client.devices.activate(device.id)print(f"Activated: {activated.status}") # activeHandle Webhook Events
Process incoming webhook notifications for receipt events, device status changes, and billing alerts.
from flask import Flask, request, jsonifyimport hmac, hashlib
app = Flask(__name__)WEBHOOK_SECRET = "whsec_your_webhook_secret"
@app.route("/webhooks/mor", methods=["POST"])def handle_webhook(): # Verify signature (EPAY-3 compliance) signature = request.headers.get("X-MOR-Signature") payload = request.get_data() expected = hmac.new( WEBHOOK_SECRET.encode(), payload, hashlib.sha256, ).hexdigest()
if not hmac.compare_digest(signature, expected): return jsonify({"error": "Invalid signature"}), 401
event = request.get_json()
match event["type"]: case "receipt.created": print(f"Receipt {event['data']['fiscal_code']} created") case "device.suspended": print(f"Device {event['data']['serial_number']} suspended") case "billing.payment_failed": print(f"Payment failed for invoice {event['data']['invoice_id']}")
return jsonify({"received": True}), 200Generate Z-Report
Close the fiscal day and generate a Z-Report. Mandatory daily per Proclamation 983/2016 (ZRPT-1). Must be submitted within 72 hours (ZRPT-2).
from mor_sdk import MorClient
client = MorClient(api_key="sk_live_your_api_key")
# Get current X-Report (real-time summary)x_report = client.devices.x_report("device-uuid")print(f"Receipts today: {x_report.receipt_count}")print(f"Total sales: ETB {x_report.total_sales}")
# Generate Z-Report (closes fiscal day — ZRPT-1)z_report = client.devices.z_report("device-uuid")print(f"Z-Report #{z_report.report_number}")print(f"Period: {z_report.period_start} to {z_report.period_end}")print(f"Total: ETB {z_report.grand_total}")
# Z-Reports are immutable after generation (ZRPT-3)# Sequential numbering enforced — no gaps (ZRPT-4)Verify Receipt Chain
Validate a receipt's digital signature and chain integrity. Every receipt references its predecessor via previous_receipt_hash (CHAIN-1).
from mor_sdk import MorClient
client = MorClient(api_key="sk_live_your_api_key")
# Verify receipt signature and chain integrityresult = client.receipts.verify("receipt-uuid")
if result.valid: print("✅ Receipt chain is valid") print(f" Signature: verified") print(f" Chain hash: matches previous receipt")else: print("❌ Chain validation failed") print(f" Error: {result.error}")
# Public verification (no API key needed — for customers)public_result = client.receipts.verify_public( fiscal_code="MOR-2026-001234")print(f"Public verification: {public_result.valid}")JavaScript / Fetch API
Integration example using plain JavaScript fetch. Suitable for Node.js, Deno, or browser environments.
// Create a receipt using fetch APIconst response = await fetch('https://api.mor.gov.et/v1/receipts', { method: 'POST', headers: { 'Authorization': 'Bearer sk_live_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ merchant_tin: '0012345678', device_id: 'device-uuid-here', items: [ { description: 'Ethiopian Coffee (1kg)', quantity: 2, unit_price: '350.00', vat_rate: 15, }, ], payment_method: 'ETHQR', total: '700.00', }),});
if (!response.ok) { const error = await response.json(); // Handle structured error: { error: "CODE", message: "..." } console.error(`${error.error}: ${error.message}`); // Handle rate limiting (SEC-7) if (response.status === 429) { const retryAfter = response.headers.get('Retry-After'); console.log(`Rate limited. Retry after ${retryAfter}s`); }} else { const receipt = await response.json(); console.log(`Fiscal Code: ${receipt.fiscal_code}`);}Need more help integrating?
Check out the full SDK documentation or start with the quickstart guide.