Skip to content

Latest commit

 

History

History
85 lines (62 loc) · 7.86 KB

File metadata and controls

85 lines (62 loc) · 7.86 KB

Integrations and Background Jobs

External systems are adapters behind application-owned ports. The default configuration is intentionally local and deterministic so the complete commerce flow can run without third-party accounts.

Provider boundary

flowchart LR
    Domain[Application/domain service]
    Port[Application-owned interface]
    Mock[Mock or console adapter]
    Real[Future real adapter]
    SaaS[Provider API]

    Domain --> Port
    Port --> Mock
    Port --> Real
    Real --> SaaS
Loading
Concern Port responsibilities Default Intended adapters
IAM introspection, profile lookup, organizations, grants, capability assignment self-hosted ZITADEL ZitadelIamProvider
Payment create, verify, query and refund a payment mock Razorpay, Cashfree
Shipping serviceability, rates, shipment/AWB, pickup, tracking, cancellation, return mock Shiprocket
Settlement/payout create/query payout using idempotency reference mock provider-supported marketplace payout rail
Email render/send transactional email console/mock Resend, SES
SMS send transactional text/OTP console/mock MSG91, Twilio
KYC create/query an external verification mock/reference only compliant KYC vendor
Media accept metadata/provider references local/reference only Cloudinary, S3-compatible storage
Search keyword/filter/sort published catalog PostgreSQL-backed public queries; in-memory port adapter available PostgreSQL full-text, Algolia, Typesense, Meilisearch, OpenSearch
Support associate and sync external case reference local/reference only Freshdesk/Zendesk-style adapter

The repository does not contain real provider credentials and does not process raw card data. Empty real-provider variables in .env.example are documentation placeholders, not active implementations.

Modules depend on application tokens rather than provider classes. The global ZitadelModule binds IAM_PROVIDER to ZitadelIamProvider; ordinary integrations use PAYMENT_PROVIDER, SHIPPING_PROVIDER, SETTLEMENT_PROVIDER, EMAIL_PROVIDER, SMS_PROVIDER, KYC_PROVIDER, MEDIA_PROVIDER, SEARCH_PROVIDER, and SUPPORT_PROVIDER. Business modules therefore do not import ZITADEL transport types or third-party SDK models.

Payment webhook contract

The mock webhook adapters use WEBHOOK_MOCK_SECRET. The signature is the lowercase hexadecimal HMAC-SHA256 of the exact raw payload; an optional sha256= prefix is accepted. Test helpers can sign payloads, but bypassing verification is not a supported development shortcut. Real adapters need their own signature algorithm, timestamp/replay checks, event mapping, and verification call where the provider requires it.

BullMQ

Redis-backed jobs are used only when retry, delay, or isolation from the request latency is useful. Representative workloads are:

  • transactional notification delivery;
  • unpaid inventory reservation expiry;
  • retryable provider follow-up;
  • periodic payment/settlement reconciliation;
  • settlement eligibility evaluation;
  • retryable vendor-organization/project-grant and staff capability provisioning.

Workers currently run inside the same NestJS process as the HTTP API. This preserves one deployable modular monolith, but API replica count also controls worker concurrency. A future deployment may add a separate entry point from the same codebase when independent worker scaling or failure isolation is justified.

The concrete queues and job names are:

Queue Job
notification-delivery notification.send
inventory-reservation-expiry inventory.reservation.expire
payment-reconciliation payments.reconcile
settlement-evaluation settlements.evaluate
iam-provisioning iam.provisioning.process

Jobs carry stable IDs and only the minimum delivery data needed (for example notification recipient/content), never entire mutable aggregates, credentials, or raw provider secrets. A worker reloads authoritative state from PostgreSQL and verifies preconditions where the job changes commerce state. Each external side effect has a deterministic job/provider idempotency key. Default jobs attempt at most five times with exponential backoff; successful jobs are retained for a bounded period/count and failed jobs longer for diagnosis.

When ENABLE_QUEUES=false, registered non-delayed handlers execute once inline; delayed jobs and scaffold queues without registered handlers are safely skipped. This makes focused tests independent of Redis, but it does not preserve delayed scheduling: a reservation-expiry job is not retained. Environment validation therefore rejects disabled queues in production; keep them enabled for the complete checkout lifecycle.

Recommended policy for provider jobs is bounded exponential backoff with useful structured failure logs. Permanent validation errors should fail without endless retry. Production operations must alert on exhausted jobs and monitor Redis persistence/capacity.

Domain events versus jobs

An in-process event communicates that committed state changed (PaymentCaptured, OrderConfirmed, ShipmentDelivered, and similar). A listener may synchronously update an immediately related read model or enqueue a durable job. Publishing an event does not make an uncommitted transaction safe, and BullMQ is not used as a replacement for database constraints.

Adding a provider

  1. Implement the existing port in src/integrations without importing provider types into domain modules.
  2. Translate money, statuses, errors, and IDs at the adapter boundary.
  3. Add typed, validated configuration and secret injection.
  4. Implement signature verification and webhook event mapping if applicable.
  5. Add contract tests against fixtures/sandbox behavior, including duplicates and timeouts.
  6. Register adapter selection through configuration and update .env.example/operations documentation.
  7. Add reconciliation before enabling production money movement.

Provider credentials belong in the deployment secret store, never in Git, image layers, job payloads, audit metadata, or logs.