Skip to content
Resource • Architecture • Updated: Feb 2026

Integrating Payments, SMS, and Accounting: A Clean Architecture

A practical approach to integrate external services without turning your codebase into a fragile mess—using clean layers, idempotency, queues, and audit-ready records.

Payment gateways SMS delivery Accounting sync Queues + webhooks

Executive summary

The cleanest integration is one where your core business rules never depend on a vendor SDK. You isolate vendors behind adapters, run side effects via queues, and store every external interaction as an auditable record.

Goal
Replaceable vendors
Switch gateways/SMS easily.
Goal
Reliable operations
Retries, idempotency, queues.
Goal
Audit-ready
Every action is logged.

The clean architecture approach

Think in layers. Your “Domain” and “Application” layers remain stable. Gateways (payments), SMS providers, and accounting systems live in Infrastructure adapters.

Domain (Business Rules)
Invoices, payments, ledgers, notifications—pure logic, no vendor code.
Application (Use Cases)
Orchestrates steps: charge, confirm, post to accounting, notify customer.
Infrastructure (Adapters)
Payment gateway, SMS provider, accounting API—replaceable modules.
Delivery (UI/API)
Controllers, webhooks, queues—transport only, not business logic.
Rule #1

No controller should call a payment SDK directly. Controllers call a use-case service; the use-case calls interfaces; adapters implement those interfaces.

A production-grade integration flow

Payments and SMS are “side effects”. Treat them as asynchronous jobs whenever possible. Accounting sync should be consistent and auditable, not “best effort”.

1
Create invoice
Generate invoice number, amount, customer, and due date. Status: Pending.
2
Initiate payment
Create payment intent with idempotency key. Store request/response.
3
Webhook confirms payment
Verify signature, ensure idempotency, update payment status.
4
Post to accounting (queued)
Create journal entries / receipts via adapter. Retry on failure.
5
Send receipt SMS (queued)
Send SMS using templated message; log delivery status.
Why this works
  • Webhooks are verified and idempotent (safe retries).
  • Accounting and SMS run via queues (no UI slowdown).
  • Every external call is recorded (audit trail).

Idempotency: the #1 reliability tool

In payments and webhooks, requests can be repeated. Idempotency ensures repeated events don’t double-charge or double-post to accounting.

Where to apply it
  • Payment intent creation
  • Webhook processing
  • Accounting posting
  • SMS sending (avoid duplicates)
How to implement
  • Use an idempotency key = invoice_id + action
  • Store “processed events” in a table
  • Return early if already processed
  • Make posting jobs safe to retry

Common anti-patterns to avoid

Calling vendor SDK in Controllers
Hard to test; UI failures break payments; mixing transport and business rules.
Syncing accounting inside the payment callback
If accounting is slow/unavailable, you break payments and user experience.
No audit trail for external requests
You can’t answer: “What happened?”, “Who retried?”, “Which payload was sent?”
No idempotency on webhooks
Duplicate webhooks can double-post receipts or double-send SMS.

What to store for audit & debugging

You don’t need to store everything forever, but you do need enough to explain every external side effect.

Record Fields Why
payment_attempts invoice_id, idempotency_key, request, response, status Trace payment lifecycle.
webhook_events provider, event_id, payload_hash, received_at, processed_at Idempotency and replay.
accounting_posts invoice_id, journal_ref, request, response, status, retries Audit and reconciliation.
sms_messages to, template, payload, provider_msg_id, status Delivery tracking.

Need help implementing this?

ReanOrt can design the integration architecture, implement reliable webhooks and queues, and deliver audit-ready finance flows.