# Payments Integration (Moyasar, Webhooks, Idempotency) This ExecPlan is a living document. The sections `Progress`, `Surprises & Discoveries`, `Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work proceeds. The requirements for ExecPlans live in `PLANS.md` at the repository root. This document must be maintained in accordance with that file. ## Purpose / Big Picture After this change, the backend can create Moyasar payments, track their state transitions, and reconcile them via webhooks in an idempotent and auditable way. A user can create a booking payment and see it progress from initiated to paid or failed. You can see it working by creating a payment, receiving a webhook callback that marks it as paid, and observing the payment record transition with a recorded provider reference and idempotency key. ## Progress - [x] (2026-02-28 14:35Z) Created ExecPlan for payments integration (Moyasar + webhooks + idempotency). - [x] (2026-02-28 15:05Z) Inspected payments models/endpoints and aligned naming with Moyasar scaffolding. - [x] (2026-02-28 15:20Z) Defined payment state model extensions and idempotency tracking fields. - [x] (2026-02-28 15:40Z) Implemented payment creation service and API endpoint with provider gateway. - [x] (2026-02-28 15:55Z) Implemented webhook endpoint with secret verification and status mapping. - [x] (2026-02-28 16:10Z) Added tests for creation, idempotency, and webhook reconciliation. - [x] (2026-02-28 16:20Z) Updated `docs/risks.md` to close payment integration gaps once tested. ## Surprises & Discoveries - Observation: The payments gateway needed an HTTP client dependency, so `requests` was added to backend requirements. Evidence: `ModuleNotFoundError: No module named 'requests'` when running migrations after adding gateway calls. ## Decision Log - Decision: Model payment state transitions as explicit status changes with audit-friendly timestamps. Rationale: Payment flows must be auditable and deterministic under retries. Date/Author: 2026-02-28, Codex - Decision: Require idempotency keys on payment creation requests. Rationale: Prevents duplicate charges when clients retry. Date/Author: 2026-02-28, Codex - Decision: Use a dedicated webhook endpoint with signature verification. Rationale: Ensures authenticity of provider callbacks and protects state integrity. Date/Author: 2026-02-28, Codex - Decision: Store provider payloads and webhook payloads on the payment record for auditability. Rationale: Helps trace payment transitions without introducing a separate event table yet. Date/Author: 2026-02-28, Codex ## Outcomes & Retrospective Payment creation, idempotency handling, and webhook reconciliation are implemented for Moyasar. Tests cover creation, idempotency, and webhook status transitions, reducing the largest Phase 1 reliability gap. Refund/capture operations remain future work if required. ## Context and Orientation Payments live in `backend/apps/payments/` with current models and API endpoints. The system currently stores payment records but does not integrate with Moyasar or reconcile webhooks. Booking flows live in `backend/apps/bookings/` and should link to payments. The project standards require business logic in services and predictable error responses. ## Plan of Work First, review existing payment models and endpoints to avoid breaking field names. Identify whether `Payment` includes a reference to `Booking`, a `provider_reference`, and a status field. If any are missing, add them along with timestamps for `initiated_at`, `paid_at`, and `failed_at`. Create a migration for the new fields. Ensure status choices include at least `initiated`, `pending`, `paid`, `failed`, and `refunded` if refunds are in scope. Next, introduce idempotency tracking. Add a `idempotency_key` field to the payment model (unique, indexed) and validate that payment creation requests require it. If a request repeats with the same key, return the existing payment without creating a new provider charge. Then, implement the Moyasar payment creation service in `backend/apps/payments/services.py`. The service should build the provider request using amount, currency, description, and return URLs, and persist the `provider_reference` (payment id returned by Moyasar). Store the full provider response in a JSON field for audit if available. Add a dedicated API endpoint for payment creation in `backend/apps/payments/views.py` and `backend/apps/payments/urls.py`. It should: - Require authentication. - Validate booking ownership and amount. - Require `idempotency_key`. - Call the service to create the provider payment. - Return the payment record plus any provider redirect URL if applicable. Then, implement the webhook endpoint (`/api/payments/webhook/`) with signature verification using Moyasar’s secret. It should parse the event, locate the payment by `provider_reference`, apply an idempotent state transition, and record timestamps. Unknown events should be logged but return 200 to avoid retries if possible. Finally, add tests in `backend/apps/payments/tests/`: - Creating a payment succeeds and stores provider reference. - Creating with the same idempotency key returns the original record. - Webhook for `paid` updates status and timestamp. - Webhook with invalid signature is rejected. - Webhook is idempotent (replay does not change state or duplicate logs). Update `docs/risks.md` to mark payment integration gaps as addressed. ## Concrete Steps Run these commands from the repository root (`/home/m7md/kshkool/Salon`). 1. Inspect payments models and endpoints. - Read `backend/apps/payments/models.py`, `backend/apps/payments/views.py`, and `backend/apps/payments/serializers.py`. 2. Add fields for provider reference, status timestamps, and idempotency. - Update `backend/apps/payments/models.py` and create a migration. - Run: python3 backend/manage.py makemigrations payments 3. Implement services and endpoints. - Add `backend/apps/payments/services.py`. - Update serializers and views accordingly. 4. Add webhook endpoint and signature verification. - Update `backend/apps/payments/urls.py` and `backend/apps/payments/views.py`. 5. Add tests. - Create `backend/apps/payments/tests/test_payments_flow.py`. 6. Run tests. - Backend: source venv/bin/activate cd backend python3 -m pytest ## Validation and Acceptance - Creating a payment with a new idempotency key returns HTTP 201 and a provider reference. - Creating the same payment with the same idempotency key returns HTTP 200/201 with the original payment (no new provider request). - A valid webhook updates the payment status to `paid` and sets `paid_at`. - An invalid webhook signature returns HTTP 400/401 and does not mutate data. - `python3 -m pytest` passes with the new payments tests. ## Idempotence and Recovery Payment creation is safe to retry with idempotency keys. Webhook processing is idempotent and can be replayed safely. If a payment status change is applied incorrectly, it can be corrected manually via admin and will be documented in audit fields. ## Artifacts and Notes Example idempotency pattern: existing = Payment.objects.filter(idempotency_key=key).first() if existing: return existing Example overlap-safe webhook logic: if payment.status == PaymentStatus.PAID: return payment.mark_paid() ## Interfaces and Dependencies - `backend/apps/payments/models.py` must include fields: `provider_reference`, `idempotency_key`, `status`, `initiated_at`, `paid_at`, `failed_at`, and (optionally) `provider_payload` (JSON). - `backend/apps/payments/services.py` must define `create_payment_for_booking(booking, idempotency_key, request_data)` and `verify_webhook_signature(request)`. - `backend/apps/payments/views.py` must expose `PaymentCreateAPIView` and `payment_webhook` with signature verification. Plan Maintenance Note: Created on 2026-02-28 to implement Moyasar payments with idempotency and webhook reconciliation as the next Phase 1 reliability milestone. Plan Maintenance Note (Update): Marked steps complete and recorded dependency and audit decisions after implementing payments and tests on 2026-02-28.