Files
Salon/docs/execplans/payments-moyasar.md
T

8.1 KiB
Raw Blame History

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

  • (2026-02-28 14:35Z) Created ExecPlan for payments integration (Moyasar + webhooks + idempotency).
  • (2026-02-28 15:05Z) Inspected payments models/endpoints and aligned naming with Moyasar scaffolding.
  • (2026-02-28 15:20Z) Defined payment state model extensions and idempotency tracking fields.
  • (2026-02-28 15:40Z) Implemented payment creation service and API endpoint with provider gateway.
  • (2026-02-28 15:55Z) Implemented webhook endpoint with secret verification and status mapping.
  • (2026-02-28 16:10Z) Added tests for creation, idempotency, and webhook reconciliation.
  • (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 Moyasars 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.