mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 12:41:04 +00:00
Map EDR invoices onto the MoR EIMS /v1/register document and add the cryptographic transport needed to talk to core.mor.gov.et. Mapper: DTOs mirror the supplied Postman collection section by section. Tax is resolved per line via a caller-supplied resolver and throws when unresolved -- the app models no tax at all (invoice.taxAmount is always 0, invoice_lines and the rate catalogue carry no fiscal columns), so a zero-rated default would assert a tax position the codebase cannot support. Seller identity, document number, counters and previous IRN are passed in explicitly; the mapper stays pure. Transport: config, credential loading, RSA-SHA512 signing and /auth/login with an in-memory token cache. Signing reproduces the process that produced a working live token -- compact JSON of the inner request only, exact UTF-8 bytes, base64 signature, and base64 of the certificate file's exact bytes with no parsing or re-encoding. Concurrent callers share one login via an in-flight promise. Refresh is deliberately unimplemented: the collection shows an unsigned refresh body but also ships unsigned examples of calls that do require signing, so an expired token re-logs in instead. Errors normalise to EimsApiException carrying only the gateway's own error fields; secrets, signature, certificate and tokens never reach logs. Key and certificate file patterns are gitignored. Nothing calls EIMS automatically and no invoice entity, migration or UI is touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
182 lines
7.2 KiB
Plaintext
182 lines
7.2 KiB
Plaintext
# Copy to .env for local/docker compose (not committed).
|
|
PORT=3001
|
|
# @tria-plc/auditlog's client interceptor stamps every AuditLog row's
|
|
# `application` from this env var directly, bypassing MezgebModule.forRoot's
|
|
# applicationName option (package quirk). audit.controller.ts reads the same
|
|
# var when filtering reads, so this can be anything as long as it's set.
|
|
APPLICATION_NAME=freight-api
|
|
# Also required for @tria-plc/auditlog: its producer (AuditClientModule)
|
|
# reads the RMQ URL at package IMPORT time, before MezgebModule.forRoot's
|
|
# rmqUrl option ever runs, so only an env var reaches it — an in-code
|
|
# override is too late. Without this, audit events are silently dropped
|
|
# (no error, nothing published). Point it at whatever broker/vhost your
|
|
# RabbitMQ actually has a user provisioned on.
|
|
RABBITMQ_URL=amqp://localhost:5672
|
|
# GT06 GPS tracker TCP listener port (raw TCP, must be reachable by tracker SIMs). 0 disables.
|
|
GT06_TCP_PORT=5023
|
|
DB_HOST=localhost
|
|
DB_PORT=5433
|
|
DB_USER=postgres
|
|
DB_PASSWORD=
|
|
DB_NAME=edr_freight
|
|
|
|
# Telebirr payment gateway (freight merchant credentials)
|
|
TELEBIRR_BASE_URL=
|
|
TELEBIRR_WEB_BASE_URL=
|
|
TELEBIRR_FABRIC_APP_ID=
|
|
TELEBIRR_APP_SECRET=
|
|
TELEBIRR_MERCHANT_APP_ID=
|
|
TELEBIRR_MERCHANT_CODE=
|
|
TELEBIRR_NOTIFY_URL=https://freight-api.edr.et/payments/webhooks/telebirr
|
|
TELEBIRR_RETURN_URL=
|
|
TELEBIRR_TIMEOUT_EXPRESS=15m
|
|
TELEBIRR_PRIVATE_KEY=
|
|
TELEBIRR_PUBLIC_KEY=
|
|
TELEBIRR_INSECURE_TLS=false
|
|
|
|
# Public origin of the freight customer portal. Password-reset links sent to
|
|
# customers are built against this — it must be browser-reachable.
|
|
FREIGHT_PORTAL_URL=http://localhost:5173
|
|
|
|
# Portal pages the payment provider redirects the browser to after payment.
|
|
# Point these at the freight portal's public payment result routes.
|
|
PAYMENT_RETURN_URL=http://localhost:5173/payment/success
|
|
PAYMENT_FAILURE_URL=http://localhost:5173/payment/failure
|
|
|
|
# Drain tail (minutes) added to every booking pay window before anything expires:
|
|
# settlement is asynchronous, so a payment made in the window's last seconds lands
|
|
# after the deadline. Nothing is expired, no wagons are resold and no window cycle
|
|
# concludes until the tail passes. Defaults to 5 when unset.
|
|
FREIGHT_PAYMENT_DRAIN_MINUTES=5
|
|
# JWT (used by @tria-plc/api-common SharedAuthModule)
|
|
JWT_SECRET=
|
|
JWT_ACCESS_TOKEN_SECRET=
|
|
JWT_REFRESH_TOKEN_SECRET=
|
|
JWT_EXPIRES_IN=3600
|
|
# JWT expiry for @tria-plc/api-common token utils (jsonwebtoken timespan format)
|
|
JWT_ACCESS_TOKEN_EXPIRES=1h
|
|
JWT_REFRESH_TOKEN_EXPIRES=7d
|
|
|
|
# IAM seed defaults (used by @tria-plc/iamapi-common on first boot)
|
|
SUPER_ADMIN_EMAIL=superadmin@tria.com
|
|
SUPER_ADMIN_PHONE=
|
|
# Super-admin password. Falls back to DEFAULT_PASSWORD when empty.
|
|
SUPER_ADMIN_DEFAULT_PASSWORD=
|
|
DEFAULT_PASSWORD=password@tria
|
|
|
|
# IAM baseline shared with edr-passenger-api (roles, IAM app + permissions,
|
|
# position types, organization types + default units, org/unit settings, super
|
|
# admin). Replaces the seeder that shipped inside @tria-plc/iamapi-common — see
|
|
# packages/iam-seed. Seeds by DEFAULT when unset; every write is insert-only.
|
|
# Set to false to opt out.
|
|
SEED_IAM_BASELINE=true
|
|
|
|
# Freight org + staff (bookings / rule-engine IAM)
|
|
SEED_EDR_ORG=true
|
|
SEED_FREIGHT_STAFF=true
|
|
SEED_EXPORT_DJIBOUTI_INTERCHANGE_DEMO=false
|
|
|
|
# MinIO (used by @tria-plc/iamapi-common for file storage)
|
|
MINIO_ENDPOINT=localhost
|
|
MINIO_PORT=9000
|
|
MINIO_USE_SSL=false
|
|
MINIO_ACCESS_KEY=
|
|
MINIO_SECRET_KEY=
|
|
# Preset region so signed URLs are generated locally (no GetBucketLocation
|
|
# network call per sign). MinIO's default is us-east-1.
|
|
MINIO_REGION=us-east-1
|
|
|
|
# Redis
|
|
REDIS_HOST=localhost
|
|
REDIS_PORT=6379
|
|
|
|
# --- Notification broker (RabbitMQ) ---------------------------------------------
|
|
# SMS/email OTP + notifications are queued to RabbitMQ (consumed by the shared
|
|
# SMS/email services). Set RABBITMQ_ENABLED=false to skip the broker entirely
|
|
# (dev without a local broker).
|
|
RABBITMQ_ENABLED=false
|
|
RABBITMQ_URL=amqp://localhost:5672
|
|
SMS_QUEUE=sms_queue
|
|
|
|
# ── VeriFayda 2.0 (eSignet OIDC) identity verification ──────────────────────
|
|
# Disabled by default; /fayda/verification/start returns 503 until enabled.
|
|
FAYDA_ENABLED=false
|
|
FAYDA_CLIENT_ID=
|
|
FAYDA_AUTHORIZATION_ENDPOINT=
|
|
FAYDA_TOKEN_ENDPOINT=
|
|
FAYDA_USERINFO_ENDPOINT=
|
|
# Base64-encoded RSA private JWK used for the private_key_jwt client assertion
|
|
FAYDA_PRIVATE_KEY_BASE64=
|
|
# OAuth redirect_uri for MOBILE clients (must be registered with eSignet)
|
|
FAYDA_REDIRECT_URI=http://localhost:3001/api/fayda/verification/complete
|
|
# OAuth redirect_uri for WEB clients. Defaults to FAYDA_REDIRECT_URI when unset.
|
|
FAYDA_WEB_REDIRECT_URI=http://localhost:3000/fayda/callback
|
|
# OAuth redirect_uri for the customer portal (its own origin — must also be
|
|
# registered with eSignet). Defaults to FAYDA_WEB_REDIRECT_URI when unset.
|
|
FAYDA_PORTAL_REDIRECT_URI=http://localhost:5173/fayda/callback
|
|
CLIENT_ASSERTION_TYPE=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
|
|
FAYDA_SCOPE=openid profile email phone address
|
|
FAYDA_ACR_VALUES=mosip:idp:acr:generated-code
|
|
FAYDA_CLAIMS_LOCALES=en am
|
|
FAYDA_SESSION_TTL_MINUTES=10
|
|
EXPIRATION_TIME=15
|
|
ALGORITHM=RS256
|
|
EMAIL_QUEUE=email_queue
|
|
|
|
# Shared secret for service-to-service calls (payment microservice <-> freight).
|
|
# Required at boot; set ALLOW_UNAUTH_INTERNAL=true instead ONLY for local dev.
|
|
SERVICE_AUTH_TOKEN=change-me
|
|
|
|
# ── MoR EIMS e-invoicing (core.mor.gov.et) ─────────────────────────────────
|
|
# Disabled by default; every EIMS call fails fast with EIMS_NOT_CONFIGURED until enabled.
|
|
EIMS_ENABLED=false
|
|
EIMS_BASE_URL=https://core.mor.gov.et
|
|
EIMS_CLIENT_ID=
|
|
EIMS_CLIENT_SECRET=
|
|
EIMS_API_KEY=
|
|
EIMS_TIN=
|
|
# MoR-issued source-system identifiers (used once invoice registration lands)
|
|
EIMS_SYSTEM_NUMBER=
|
|
EIMS_SYSTEM_TYPE=
|
|
# Absolute paths to the INSA-issued credentials. Keep them OUTSIDE the repo; the file
|
|
# patterns are gitignored, but a path outside the working tree is safer still.
|
|
# The certificate is transmitted as base64 of this file's exact bytes — do not convert it.
|
|
EIMS_PRIVATE_KEY_PATH=
|
|
EIMS_CERTIFICATE_PATH=
|
|
# Optional tuning
|
|
EIMS_HTTP_TIMEOUT_MS=30000
|
|
EIMS_TOKEN_SKEW_SECONDS=45
|
|
|
|
# ── EIMS invoice registration (required only to register invoices) ─────────
|
|
# Seller identity: EDR's own legal details are not modelled anywhere in the DB.
|
|
# Region and Wereda are MoR *codes* (e.g. 13 / 574), not names.
|
|
EIMS_SELLER_LEGAL_NAME=
|
|
EIMS_SELLER_VAT_NUMBER=
|
|
EIMS_SELLER_PHONE=
|
|
EIMS_SELLER_EMAIL=
|
|
EIMS_SELLER_REGION=
|
|
EIMS_SELLER_WEREDA=
|
|
# Optional seller address parts; sent as null when unset.
|
|
EIMS_SELLER_CITY=
|
|
EIMS_SELLER_SUBCITY=
|
|
EIMS_SELLER_HOUSE_NUMBER=
|
|
EIMS_SELLER_LOCALITY=
|
|
# Tax treatment — REQUIRES FINANCE SIGN-OFF. The application models no tax at all
|
|
# (invoice.taxAmount is always 0), so nothing here is defaulted: registration fails
|
|
# locally, naming the missing variables, until these are set.
|
|
EIMS_TAX_CODE=
|
|
EIMS_TAX_RATE_PERCENT=
|
|
EIMS_EXCISE_TAX_VALUE=0
|
|
EIMS_INCOME_WITHHOLD_VALUE=0
|
|
EIMS_TRANSACTION_WITHHOLD_VALUE=0
|
|
# Document classification and payment presentation.
|
|
EIMS_TRANSACTION_TYPE=B2B
|
|
EIMS_NATURE_OF_SUPPLIES=Service
|
|
EIMS_PAYMENT_MODE=CASH
|
|
EIMS_PAYMENT_TERM=IMMIDIATE
|
|
EIMS_UNIT_DEFAULT=PCS
|
|
# MoR numeric country code for the buyer; our companies store the country name.
|
|
EIMS_BUYER_COUNTRY_CODE=
|
|
EIMS_CASHIER_NAME=
|
|
EIMS_SALESPERSON_NAME=
|