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

7.3 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).
  • Inspect current payments models, endpoints, and any Moyasar scaffolding to align naming.
  • Define payment state model and idempotency tracking.
  • Implement payment creation service and API endpoint.
  • Implement webhook endpoint with signature verification.
  • Add tests for creation, idempotency, and webhook reconciliation.
  • Update docs/risks.md to close payment integration gaps once tested.

Surprises & Discoveries

  • Observation: None yet. Evidence: No implementation work has started.

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

Outcomes & Retrospective

Planned changes will provide end-to-end payment creation, reconciliation via webhooks, and idempotency protections, filling the largest Phase 1 reliability gap.

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.