mirror of
https://github.com/rjNemo/rentease.git
synced 2026-06-12 13:46:51 +00:00
234 lines
11 KiB
Markdown
234 lines
11 KiB
Markdown
# Stripe Payment Sync Implementation Plan
|
||
|
||
## Overview
|
||
|
||
Enable Stripe-backed payment ingestion so card transactions flow automatically into Rentease while preserving manual cash entry. The system will store Stripe identifiers, support manual/cron backfills, and react to Stripe webhooks for real-time updates and refunds.
|
||
|
||
## Current State Analysis
|
||
|
||
- Booking payments are entered manually via `/payments/:id`, persisting `amount` and `payment_method` only; no external identifiers exist (`internal/server/handle_payments.go:16`, `internal/service/booking/payment.go:17`).
|
||
- Reports and booking summaries rely on totals from the `payments` table, especially card totals computed via SQL joins (`internal/repository/booking/pg_store.go:65`, `internal/service/booking/report.go:76`).
|
||
- No Stripe configuration or drivers; `config.Config` lacks keys, and `main.go` wires only database, parser, pdf clients (`internal/config/config.go:11`, `main.go:52`).
|
||
- Routes bundle payment handlers inside auth-protected groups, meaning Stripe webhooks need a dedicated public endpoint (`internal/server/routes.go:8`).
|
||
- Cron binary exists for scheduled jobs but has no payment sync job (`pkg/cron/cron.go:9`, `cmd/cron/main.go:13`).
|
||
|
||
## Desired End State
|
||
|
||
- `payments` records include Stripe IDs and status for card transactions, allowing idempotent updates and refund tracking.
|
||
- Configurable Stripe client (secret key, webhook secret) powers both range-based syncs and webhook ingestion.
|
||
- Manual sync endpoint/job triggers Stripe List APIs for a given time window and upserts records; the job can be run via API or mounted in the cron scheduler.
|
||
- Public webhook endpoint verifies Stripe signatures and handles `payment_intent.succeeded` and `charge.refunded`, updating local payments accordingly.
|
||
- Manual payment modal remains for cash/cheque/transfer; card payments can now be imported automatically.
|
||
|
||
### Key Discoveries
|
||
|
||
- Manual payment creation flow is htmx-based and fully server-side (`internal/view/booking_by_id.templ:130`).
|
||
- Repository lacks idempotent upsert capability; new helper needed to match on Stripe ID (`internal/repository/booking/pg_store.go:150`).
|
||
- `config.Host.PaymentMethods` already enumerates methods shown in UI; Stripe integration must normalise to these values (`internal/config/host.go:41`).
|
||
|
||
## Out of Scope
|
||
|
||
- Customer-level Stripe data (customer objects, saved cards).
|
||
- UI changes for displaying Stripe-specific metadata beyond status/method.
|
||
- Handling non-card Stripe events (disputes, payouts) beyond refunds.
|
||
|
||
## Implementation Approach
|
||
|
||
Augment the data model with Stripe identifiers, introduce a Stripe driver/service for syncing, expose both manual and scheduled sync triggers, and add a webhook for near real-time updates. Ensure idempotency via unique Stripe IDs and keep manual cash entry unaffected.
|
||
|
||
## Phase 1: Data Model & Repository Extensions
|
||
|
||
### Overview
|
||
|
||
Add fields to persist Stripe metadata and repository logic for idempotent upserts based on Stripe IDs.
|
||
|
||
### Changes Required
|
||
|
||
**File**: `internal/service/booking/models.go`
|
||
**Changes**: Add `StripePaymentID *string` and `StripeStatus *string` fields to `Payment` (nullable to support manual entries) with unique index on Stripe ID. Ensure `gorm` tags reflect uniqueness and index requirements.
|
||
|
||
**File**: `internal/repository/booking/pg_store.go`
|
||
**Changes**:
|
||
|
||
- Update `CreatePayment` to respect new fields.
|
||
- Add `UpsertStripePayment` method: find by `stripe_payment_id`, update status/amount/method if exists; otherwise create.
|
||
- Update `GetPayment`/`UpdatePayment` logic to preload new fields.
|
||
|
||
**File**: `main.go`
|
||
**Changes**: Trigger `database.Migrate` after model changes to add new columns (AutoMigrate will handle column additions).
|
||
|
||
**File**: `scripts/payment_migration.sql`
|
||
**Changes**: (Optional) Document SQL backfill to populate Stripe IDs later if needed.
|
||
|
||
```go
|
||
// Payment model sketch
|
||
type Payment struct {
|
||
gorm.Model
|
||
BookingID uint `gorm:"not null;index"`
|
||
Booking Booking `gorm:"foreignKey:BookingID;constraint:OnDelete:CASCADE"`
|
||
Amount float64
|
||
PaymentMethod config.PaymentMethod
|
||
StripePaymentID *string `gorm:"uniqueIndex"`
|
||
StripeStatus *string
|
||
}
|
||
```
|
||
|
||
### Success Criteria
|
||
|
||
#### Automated Verification
|
||
|
||
- [x] `go test ./internal/service/booking/...`
|
||
- [x] `go test ./internal/repository/booking/...`
|
||
- [x] `golangci-lint run`
|
||
|
||
#### Manual Verification
|
||
|
||
- [ ] Run application; AutoMigrate adds new columns without data loss.
|
||
- [ ] Existing manual payment entry still works and shows in UI.
|
||
|
||
## Phase 2: Stripe Client & Sync Service
|
||
|
||
### Overview
|
||
|
||
Introduce Stripe SDK usage, configuration, and a service method to fetch payments for a time range, process them, and upsert into storage. Expose a job callable via API and cron.
|
||
|
||
### Changes Required
|
||
|
||
**File**: `go.mod`
|
||
**Changes**: Add `github.com/stripe/stripe-go/v83` dependency (or latest version) plus `go mod tidy`.
|
||
|
||
**File**: `internal/config/config.go`
|
||
**Changes**: Add fields `StripeSecretKey`, `StripeWebhookSecret`, optional `StripeConnectAccount` (if needed). Update env tags and documentation.
|
||
|
||
**File**: `internal/driver/stripe/client.go` (new)
|
||
**Changes**: Implement wrapper around Stripe client to list `PaymentIntent` (or `Charge`) objects within a `Created` range, retrieving status, amount, currency, and metadata/booking reference if available.
|
||
|
||
**File**: `internal/service/booking/payment.go`
|
||
**Changes**: Add `SyncStripePayments(ctx, params)` method that:
|
||
|
||
- Calls driver to list payments between `from`/`to` timestamps.
|
||
- Maps Stripe payment method/status to internal values.
|
||
- Resolves booking association (e.g., via metadata `booking_id` or manual mapping; define behaviour if not found).
|
||
- Calls repository `UpsertStripePayment` (with fallback logging if booking unknown).
|
||
|
||
**File**: `internal/service/booking/service.go`
|
||
**Changes**: Extend interface with `UpsertStripePayment` and register driver in constructor (add parameter for Stripe client).
|
||
|
||
**File**: `cmd/cron/main.go`
|
||
**Changes**: Add job (e.g., `StripePaymentSync`) that invokes service with last-run timestamp stored persistently (file/db) or via parameter. For initial implementation, fetch daily by default.
|
||
|
||
**File**: `pkg/cron/cron.go`
|
||
**Changes**: Optionally enhance scheduler to accept context cancellation or job-specific config; ensure job invocation errors bubble through channels.
|
||
|
||
**File**: `internal/server/handle_api_sync.go`
|
||
**Changes**: Add new handler `handleStripeSync(bs *booking.Service)` bound to `POST /api/stripe/sync` expecting payload (from, to). Validate API key via existing middleware and trigger sync job synchronously (or enqueue background routine).
|
||
|
||
### Success Criteria
|
||
|
||
#### Automated Verification
|
||
|
||
- [x] Unit tests mocking Stripe client to validate `SyncStripePayments` mapping and repository calls.
|
||
- [x] Route handler tests confirming 401 protection and sync invocation (can use Echo context test helpers).
|
||
- [x] `go test ./cmd/cron` for scheduler wiring.
|
||
|
||
#### Manual Verification
|
||
|
||
- [ ] Configure Stripe test key; run API sync for a test window; verify payments appear in booking detail.
|
||
- [ ] Cron job logs success when run with shortened schedule in dev.
|
||
|
||
## Phase 3: Webhook Endpoint & Event Processing
|
||
|
||
### Overview
|
||
|
||
Accept Stripe webhook notifications to keep payment status current (successes and refunds) with signature verification.
|
||
|
||
### Changes Required
|
||
|
||
**File**: `internal/server/routes.go`
|
||
**Changes**: Register `POST /webhooks/stripe` prior to auth middleware so Stripe can reach it; ensure CORS is acceptable.
|
||
|
||
**File**: `internal/server/handle_stripe_webhook.go` (new)
|
||
**Changes**:
|
||
|
||
- Parse body, verify signature using `StripeWebhookSecret` via Stripe SDK helper.
|
||
- Handle `payment_intent.succeeded`: locate booking association (metadata); call service upsert with status `Succeeded`.
|
||
- Handle `charge.refunded` (and partial refunds) by updating payment amount/status (set `StripeStatus = "refunded"`, adjust amounts if partial, record refund metadata).
|
||
- Return 2xx quickly; log and capture errors.
|
||
|
||
**File**: `internal/service/booking/payment.go`
|
||
**Changes**: Add helper `ApplyStripeEvent` to centralise webhook logic, reuse upsert.
|
||
|
||
**File**: `internal/config/config.go`
|
||
**Changes**: Document required webhook secret env var.
|
||
|
||
**File**: `main.go`
|
||
**Changes**: Inject Stripe client + webhook secret into server (extend `server.New` options or add to booking service).
|
||
|
||
### Success Criteria
|
||
|
||
#### Automated Verification
|
||
|
||
- [x] Unit tests for webhook handler verifying signature failure -> 400, success -> upsert call.
|
||
- [x] Service tests ensuring refunds update amounts correctly.
|
||
|
||
#### Manual Verification
|
||
|
||
- [ ] Use Stripe CLI `stripe listen` to forward events locally; confirm succeeded and refund events update bookings.
|
||
- [ ] Invalid signature events rejected (check logs).
|
||
|
||
## Phase 4: Admin Controls, UI Confirmation, and Regression Testing
|
||
|
||
### Overview
|
||
|
||
Expose manual triggers, ensure UI reflects new automatic payments, and validate the mixed workflow.
|
||
|
||
### Changes Required
|
||
|
||
**File**: `internal/view/booking_by_id.templ`
|
||
**Changes**: Optionally add indicator for Stripe-synced payments (e.g., status badge) while keeping manual modal intact.
|
||
|
||
**File**: `internal/server/handle_payments.go`
|
||
**Changes**: Ensure manual flow still sets `StripePaymentID`/`StripeStatus` nil. Consider preventing duplicate manual card entries by clarifying methods.
|
||
|
||
**File**: `docs/` (new doc)
|
||
**Changes**: Document Stripe setup, env vars, webhook configuration, manual sync API usage, cron schedule.
|
||
|
||
**File**: `internal/server/handle_reports.go`
|
||
**Changes**: Confirm card totals include Stripe payments; adjust if status filtering needed (e.g., exclude refunded amounts from revenue).
|
||
|
||
### Success Criteria
|
||
|
||
#### Automated Verification
|
||
|
||
- [ ] Snapshot or template tests (if available) confirm payment list includes expected fields.
|
||
- [ ] `go test ./internal/view/...` (if templ tests) or run `templ generate` to ensure templates compile.
|
||
|
||
#### Manual Verification
|
||
|
||
- [ ] Booking page shows Stripe-imported payment with correct method/status.
|
||
- [ ] Manual cash entry unaffected.
|
||
- [x] Documentation reviewed for completeness.
|
||
|
||
## Testing Strategy
|
||
|
||
- Unit tests mocking Stripe client and repository behaviour to ensure idempotency and status handling.
|
||
- Integration test hitting `/api/stripe/sync` with fake Stripe driver to verify end-to-end upsert (could use in-memory driver for tests).
|
||
- Webhook handler tests using Stripe’s official test helpers for signature verification.
|
||
- Manual QA in Stripe test mode: create payment intent with metadata linking to booking ID, trigger success/refund, verify UI/report updates.
|
||
|
||
## Performance Considerations
|
||
|
||
- Stripe list API pagination: ensure sync job handles pagination and rate limits (backoff on errors).
|
||
- Webhook handler should be lightweight; consider asynchronous processing (goroutine or queue) if high volume, though initial volume likely low.
|
||
|
||
## Migration Notes
|
||
|
||
- AutoMigrate adds nullable columns; ensure production deploy runs with Stripe env vars set (or feature toggled off until configured).
|
||
- Consider backfilling existing Stripe payments manually post-deploy using the new API sync endpoint.
|
||
|
||
## References
|
||
|
||
- Research doc: `thoughts/shared/research/2025-10-03-stripe-payment-sync.md`
|
||
- Manual payment handler: `internal/server/handle_payments.go:16`
|
||
- Booking service payment logic: `internal/service/booking/payment.go:17`
|
||
- Reports card totals: `internal/repository/booking/pg_store.go:65`
|