remove temp files

This commit is contained in:
Atharva Deosthale
2025-11-13 01:32:01 +05:30
parent eb08a3379a
commit 3967b2e338
4 changed files with 0 additions and 1157 deletions
-131
View File
@@ -1,131 +0,0 @@
## Payments Revamp Plan
### 1. Background & Review Feedback
- PR [#10573](https://github.com/appwrite/appwrite/pull/10573) review (eldadfux) requires moving subscription logic into a dedicated `v1/payments` service, aligned with post-1.7 module architecture and REST plural conventions.
- Avoid extending `/v1/account` and prevent Stripe vendor lock-in; design must allow alternate providers ("UltraPayments" mention) and attach subscriptions to both users and teams.
- Internal usage tracking is still required for quota enforcement while syncing with external billing systems.
### 2. Current Draft Gaps (auth-subscriptions branch)
- Controllers: `app/controllers/api/account.php` and `api/auth-plans.php` house payments logic, diverging from modular HTTP action classes (`src/Appwrite/Platform/Modules/*/Http/...`).
- Data Coupling: `app/config/collections/platform.php` and `collections/common.php` now embed Stripe-specific columns (`authStripeSecretKey`, `stripeCustomerId`, etc.), making migration to other providers costly.
- Service Registry: `app/config/services.php` declares an `authPlans` controller entry rather than using a module-driven service (contrast with `functions`/`sites` entries that leave `controller` empty and rely on modules).
- User Records: Subscription status fields live directly on `users`, preventing team sharing and complicating multi-actor ownership.
- Tests & Specs: No module-style route tests or worker coverage; OpenAPI specs patched manually via controller paths.
### 3. Architectural Principles to Mirror
- **Module Composition:** Each platform module provides `Module.php` registering `Services/Http.php` (and optional `Services/Workers.php`) (e.g., `src/Appwrite/Platform/Modules/Functions` and `Sites`). Classes extend `Utopia\Platform\Service`, where HTTP service constructors call `addAction()` for each route class (`Create`, `Get`, `XList`, etc.).
- **Route Classes:** HTTP actions extend `Appwrite\Platform\Modules\Compute\Base` (where applicable), use the `HTTP` trait, describe metadata (scopes, events, SDK information), inject dependencies, and implement an `action()` method (see `Functions/Http/Functions/Create.php` and `Sites/Http/Deployments/Template/Create.php`).
- **Platform Bootstrap:** `src/Appwrite/Platform/Appwrite.php` registers modules. New services must follow this pattern so the factory loads them automatically.
- **Collections:** Core metadata uses JSON blobs for provider-specific config (e.g., `projects` collection attributes `services`, `auths`, `smtp` with `json` filter) instead of expanding columns per provider. Use this precedent when storing payments integrations.
- **Testing:** Route behavior is covered via module-specific tests under `tests/e2e/Services` and unit tests for validators. Payment routes must follow same structure.
### 4. Implementation Roadmap
#### Phase A – Module Bootstrapping
- Create `src/Appwrite/Platform/Modules/Payments/Module.php` mirroring `Functions\Module`/`Sites\Module`, registering an HTTP service and (if needed) worker service(s).
- Add `src/Appwrite/Platform/Modules/Payments/Services/Http.php`; register route action classes (Plans, Subscriptions, Usage, Configuration). Maintain naming parity: `Plans\Create.php`, `Plans\Update.php`, `Plans\XList.php`, etc.
- Wire module in `src/Appwrite/Platform/Appwrite.php` via `$this->addModule(new Payments\Module());`.
- Update DI autoload if required (Composer PSR-4 should already include `src/Appwrite`).
#### Phase B – REST Surface Definition
- Design plural endpoints under `/v1/payments/...` (e.g., `/v1/payments/plans`, `/v1/payments/subscriptions`, `/v1/payments/subscriptions/{subscriptionId}`, `/v1/payments/providers`).
- Each route should expose scope labels (new `payments.*` scopes) and SDK metadata similar to `Functions` routes. Provide responses using dedicated models (plan, subscription, usage event) under `src/Appwrite/Utopia/Response/Model`.
- Implement both project-admin endpoints (plan management) and authenticated actor endpoints (user/team subscription management). Accept `actorType` and `actorId` to allow teams or users.
- Remove or deprecate `/v1/account/subscription*` routes, replacing them with thin adapters or erroring with migration notice once clients are updated.
#### Phase C – Scope, Permissions & Service Registry
- Update `app/config/scopes.php` to introduce `payments.read`, `payments.write`, `payments.subscribe`, and console equivalents. Ensure console/admin scopes map to UI flows.
- Amend `app/config/services.php` to add a `payments` entry with empty `controller` (module-managed), similar to `functions` and `sites`, and expose docs metadata when available.
- Ensure new scopes appear in e2e scope tests (`tests/e2e/Scopes/`) and update SDK generation metadata if necessary.
#### Phase D – Data Model Restructuring
- Introduce `app/config/collections/payments.php` (or reuse existing file with new namespace) describing:
- `payments_plans`: plan metadata, generic fields (name, price(s), billing cycle, features) plus `providers` JSON mapping provider IDs/handles.
- `payments_features`: reusable feature definitions with type (`boolean`, `metered`), description, and provider metadata (if applicable).
- `payments_plan_features`: join table storing feature assignments per plan with tiering/usage caps; provider-specific IDs live inside a `providers` JSON object.
- `payments_subscriptions`: subscription records linking `projectId`, `actorType` (`user`/`team`), `actorInternalId`, `planId`, `status`, `lifecycle timestamps`, `quota progress`, and `providers` JSON (customer IDs, subscription IDs, invoice sync state).
- `payments_usage_events`: internal usage ledger capturing increments, reconciliation status, and optional provider receipt IDs.
- Remove Stripe-specific attributes from `app/config/collections/platform.php` and `common.php`; replace with:
- `projects.payments` JSON attribute storing provider configuration (`{"providers": {"stripe": {...}}}`) with `json` and `encrypt` filters for sensitive values.
- Users/teams should no longer carry plan IDs or Stripe identifiers; instead, join via `payments_subscriptions`.
- Provide migration helpers to seed existing data into new collections (see Phase G).
#### Phase E – Provider Abstraction Layer
- Define `src/Appwrite/Payments/Provider/ProviderAdapterInterface` encapsulating operations used today (`ensureProduct`, `ensurePrice`, `ensureMeter`, `createCheckoutSession`, `createPortalSession`, `switchPlan`, `cancel`, `reportUsage`, etc.).
- Move `src/Appwrite/Auth/Subscription/StripeService.php` into `src/Appwrite/Payments/Provider/StripeAdapter.php`, implementing the new interface. Update namespaces and dependency injection accordingly.
- Introduce a provider registry/factory (e.g., `src/Appwrite/Payments/Provider/Registry.php`) to resolve adapters based on project configuration (`payments.providers` JSON). Support multiple providers by iterating adapters when needed.
- Relocate validator/exception classes under `src/Appwrite/Payments/` namespace (`Validator/StripeKey.php` → `Payments/Validator/StripeKey.php`) and replace Stripe-specific names with provider-neutral naming where possible.
#### Phase F – Business Logic Implementation
- Plan CRUD: Route classes should validate payloads, persist records in `payments_plans`, and delegate provider provisioning via the adapter interface (creating products/prices/meters for configured providers).
- Feature assignment: Manage assignments in `payments_plan_features`, including meter provisioning and tiered pricing via provider adapters.
- Subscription lifecycle: Implement routes for creating/updating/canceling subscriptions. Logic should:
- Resolve actor (user/team) and existing subscription record.
- Ensure plan compatibility and usage caps.
- Call provider adapter to create/update external subscription IDs.
- Persist status, period boundaries, and cancellation flags in `payments_subscriptions`.
- Usage enforcement: Provide endpoints/services to increment usage (`payments_usage_events`), compute current consumption, and determine cap breaches synchronously for feature gating. Expose summary endpoints to clients.
- Configuration flows: Rebuild project-level configuration endpoints (`/v1/projects/:projectId/payments/providers`) under the module, enabling/disabling providers and storing credentials in `projects.payments` JSON. Ensure `StripeAdapter` initialization replicates existing `initializeAccount()` behavior.
#### Phase G – Background Processing & Workers
- If asynchronous tasks are required (e.g., syncing usage to providers, handling webhooks), create `src/Appwrite/Platform/Modules/Payments/Services/Workers.php` and worker action classes (pattern from `Functions/Services/Workers.php`).
- Hook into existing queues (`queueForEvents`, `queueForStatsUsage`, etc.) where appropriate to emit events/audits similar to other services.
- Evaluate need for cron tasks to reconcile usage or handle grace periods.
#### Phase H – Migration & Backward Compatibility
- Author migration script (CLI task under `src/Appwrite/Platform/Tasks` or a dedicated maintenance script) to:
- Read existing `auth_plans`, `auth_plan_features`, and user subscription fields.
- Populate new `payments_*` collections and JSON config fields.
- Update user/team documents to remove deprecated attributes.
- Provide database migration documentation and fallback strategy (e.g., how to disable payments before upgrade).
- For installations already using the draft branch, document manual steps to transfer customer data.
#### Phase I – Specification & SDK Updates
- Regenerate OpenAPI/Swagger specs via existing tooling (`bin/specs` / `src/Appwrite/Platform/Tasks/Specs.php`) after new routes are in place. Confirm console/client/server specs include `payments` namespace.
- Update SDK code generation inputs and ensure new endpoints appear in client/server SDKs with correct grouping.
- Adjust console UI integrations to consume the new endpoints and models (plan lists, subscription management, usage dashboards).
#### Phase J – Testing Strategy
- **Unit Tests:**
- Adapter tests mocking Stripe client interactions.
- Validators under `tests/unit/Payments/Validator/*`.
- **Module Route Tests:**
- Add new classes in `tests/e2e/Services/Payments/` covering admin plan management, actor subscription flows, and usage queries for both users and teams.
- Include negative cases (disabled provider, cap exceeded, missing permissions).
- **Integration Tests:**
- Mock provider webhook handling and ensure idempotency.
- Test migrations by seeding legacy data and verifying conversion results.
- Update benchmark/security CI workflows if the new module introduces additional containers or environment variables.
#### Phase K – Documentation & Developer Experience
- Draft developer docs (`/docs/services/payments.md`) describing configuration, APIs, and migration steps.
- Update console copy and CLI help text to match new terminology (plans, features, subscriptions, usage events).
- Provide sample scripts for common tasks (e.g., creating plans via CLI/SDK).
### 5. Open Questions / Follow-Up Items
- **Multi-provider Support:** Should multiple providers operate simultaneously per project or is it a single active provider? Decide how `providers` JSON is structured (array vs keyed map).
- **Team Billing Semantics:** Clarify how team-owned subscriptions map to team members—do all members share quotas automatically? Need UX decisions in console.
- **Usage Reconciliation:** Define authoritative source when provider usage differs from internal tracking; plan for dispute resolution.
- **Webhook Processing:** Determine whether payments module handles Stripe webhooks or leverages existing generic webhook processor; adjust architecture accordingly.
- **Legacy Clients:** Agree on deprecation timeline for `/v1/account/subscription*` endpoints and provide compatibility shims if required.
### 6. Next Steps
- Validate this plan with stakeholders (product + engineering) to confirm scope.
- Sequence implementation into incremental PRs (module scaffolding → data model → provider abstraction → endpoint migration → migrations/tests).
- Reserve time for console integration and documentation updates before GA release.
-306
View File
@@ -1,306 +0,0 @@
## Payments Revamp Detailed Blueprint
### Executive Summary
- Replace draft `/v1/account/subscription*` endpoints with a dedicated `/v1/payments` service implemented as an Appwrite platform module, satisfying Eldad’s review.
- Deliver provider-agnostic APIs and data model so multiple payment vendors can plug in without breaking clients.
- Introduce robust internal usage controls and enable subscriptions for both users and teams, matching multi-tenant requirements.
### 1. Service Architecture
1. **Module Registration**
- Add `Appwrite\Platform\Modules\Payments\Module` mirroring existing modules (`Functions`, `Sites`).
- Register in `Appwrite\Platform\Appwrite::__construct` via `$this->addModule(new Payments\Module());`.
- Provide `Services/Http.php` and optional `Services/Workers.php` classes inheriting `Utopia\Platform\Service`.
- Use registrar pattern if route count grows (similar to `Databases\Services\Registry`).
2. **Route Classes (per Endpoint)**
- Location: `src/Appwrite/Platform/Modules/Payments/Http/...`.
- Each class extends `Appwrite\Platform\Modules\Compute\Base` or a lighter abstract if no compute needs, uses `use HTTP;`, configures metadata (`setHttpMethod`, `setHttpPath`, `desc`, `groups`, `label`, `param`).
- Inject dependencies via `->inject()` matching established pattern (response, db, queue, project, user/team documents, adapter registry).
- Return models defined in `src/Appwrite/Utopia/Response/Model` (new models for payments entities).
3. **SDK & Metadata Integration**
- Add `->label('sdk', new Method(...))` for each route to define namespace/group (`payments`) and auth types.
- Define new permission scopes (`payments.read`, `payments.write`, `payments.subscribe`) and map in `app/config/scopes.php` for client, server, console contexts.
- Ensure each action sets `resourceType`, `event`, `audits.*` labels where relevant to integrate with events/auditing infrastructure.
### 2. API Surface
Below is the proposed REST contract (subject to iterative refinement). All endpoints live under `/v1/payments` unless noted.
| Endpoint | Method | Description | Auth scopes | Notes |
| --------------------------------------------------- | ------ | ----------------------------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------- |
| `/v1/payments/plans` | POST | Create plan | `payments.write` (project admin) | Accepts plan metadata, pricing options, feature definitions. Returns plan model. |
| `/v1/payments/plans` | GET | List plans | `payments.read` | Supports queries/filtering; returns paginated list. |
| `/v1/payments/plans/:planId` | GET | Get single plan | `payments.read` | |
| `/v1/payments/plans/:planId` | PUT | Update plan | `payments.write` | Provider-specific updates routed through adapters while preserving API stability. |
| `/v1/payments/plans/:planId` | DELETE | Archive/deactivate plan | `payments.write` | Soft-delete toggles `active=false`. |
| `/v1/payments/plans/:planId/features` | POST | Assign/Reconfigure features | `payments.write` | Handles metered tiers, usage caps. |
| `/v1/payments/plans/:planId/features` | GET | List assigned features | `payments.read` | |
| `/v1/payments/plans/:planId/features/:featureId` | DELETE | Remove feature | `payments.write` | |
| `/v1/payments/features` | POST | Create reusable feature definition | `payments.write` | Feature types: `boolean`, `metered`, potential future types. |
| `/v1/payments/features` | GET | List features | `payments.read` | |
| `/v1/payments/features/:featureId` | PUT | Update feature | `payments.write` | |
| `/v1/payments/features/:featureId` | DELETE | Archive feature | `payments.write` | |
| `/v1/payments/providers` | GET | Get project payments configuration | `payments.read` (admin) | Returns provider statuses, capabilities. |
| `/v1/payments/providers` | PUT | Configure providers | `payments.write` (admin) | Accepts provider credentials/options; triggers adapter bootstrap (webhooks, products). |
| `/v1/payments/providers/:providerId/actions/test` | POST | Validate provider credentials | `payments.write` | Optional sanity check endpoint per provider. |
| `/v1/payments/subscriptions` | POST | Create subscription | `payments.subscribe` (actor) | Body contains `actorType` (`user`/`team`), `actorId`, `planId`, optional payment options. |
| `/v1/payments/subscriptions` | GET | List subscriptions | `payments.read` | Filters: actor, plan, status, provider. |
| `/v1/payments/subscriptions/:subscriptionId` | GET | Retrieve subscription | `payments.read` | Validates access (actor membership, admin). |
| `/v1/payments/subscriptions/:subscriptionId` | PATCH | Update subscription (plan switch, cancel) | `payments.subscribe` or `payments.write` | Handles upgrade/downgrade, cancellation, metadata updates. |
| `/v1/payments/subscriptions/:subscriptionId/cancel` | POST | Explicit cancel endpoint | `payments.subscribe` | Sets cancel at period end or immediate cancel. |
| `/v1/payments/subscriptions/:subscriptionId/resume` | POST | Resume cancelled subscription | `payments.subscribe` | |
| `/v1/payments/subscriptions/:subscriptionId/usage` | GET | Get detailed usage summary | `payments.read` | Combines internal events with provider sync status. |
| `/v1/payments/subscriptions/:subscriptionId/usage` | POST | Report usage event | `payments.subscribe` (server key) | For services to emit usage increments (e.g., rate limiting). Supports batching. |
| `/v1/payments/usage/events` | GET | Query raw usage events | `payments.read` | For analytics/monitoring. |
| `/v1/payments/usage/reconcile` | POST | Trigger reconciliation with provider | `payments.write` | Optional admin action to force sync. |
| `/v1/payments/webhooks/:providerId` | POST | Provider webhook endpoint (internal) | Auth via signature | Each provider adapter registers handler. Routed outside public docs. |
**Compatibility:**
- Terminate legacy `/v1/account/subscription*` routes after transitional period. Provide compatibility wrappers that call `payments` module while returning same payload shape to avoid immediate SDK breakage.
### 3. Provider Plug-in System
1. **Core Concepts**
- Interface: `src/Appwrite/Payments/Provider/Adapter.php` (or `ProviderAdapterInterface`) defines actions consumed by module.
- Responsibilities: product/price lifecycle, customer management, subscription lifecycle, metered usage reporting, invoice retrieval, webhook verification, credential validation.
2. **Adapter Interface Sketch**
```php
<?php
namespace Appwrite\Payments\Provider;
use Utopia\Database\Document;
interface Adapter
{
public function getIdentifier(): string; // e.g., "stripe"
public function configure(array $config, Document $project): ProviderState;
public function ensurePlan(array $planData, ProviderState $state): ProviderPlanRef;
public function updatePlan(array $planData, ProviderPlanRef $reference, ProviderState $state): ProviderPlanRef;
public function deletePlan(ProviderPlanRef $reference, ProviderState $state): void;
public function ensureFeature(array $featureData, ProviderPlanRef $plan, ProviderState $state): ProviderFeatureRef;
public function ensureSubscription(Document $actor, array $subscriptionData, ProviderState $state): ProviderSubscriptionRef;
public function updateSubscription(ProviderSubscriptionRef $subscription, array $changes, ProviderState $state): ProviderSubscriptionRef;
public function cancelSubscription(ProviderSubscriptionRef $subscription, bool $atPeriodEnd, ProviderState $state): ProviderSubscriptionRef;
public function resumeSubscription(ProviderSubscriptionRef $subscription, ProviderState $state): ProviderSubscriptionRef;
public function createCheckoutSession(Document $actor, array $planContext, ProviderState $state, array $options = []): ProviderCheckoutSession;
public function createPortalSession(Document $actor, ProviderState $state, array $options = []): ProviderPortalSession;
public function reportUsage(ProviderSubscriptionRef $subscription, string $featureId, int $quantity, \DateTimeInterface $timestamp, ProviderState $state): void;
public function syncUsage(ProviderSubscriptionRef $subscription, ProviderState $state): ProviderUsageReport;
public function handleWebhook(array $payload, ProviderState $state): ProviderWebhookResult;
public function testConnection(array $config): ProviderTestResult;
}
```
- `ProviderState`, `ProviderPlanRef`, `ProviderFeatureRef`, `ProviderSubscriptionRef`, etc., are small value objects storing provider IDs and metadata. They help hide vendor specifics from the rest of the system.
- `Document` references represent Appwrite entities (project, user, team) fetched prior to calling adapter.
3. **Registry & Resolution**
- Introduce `src/Appwrite/Payments/Provider/Registry.php` to map provider identifiers (e.g., `stripe`, `ultra`) to adapter instances. Similar to `Messaging` using adapter classes.
- Registry loads adapters lazily and caches per project if necessary.
- Module obtains provider configuration from `project` document (see data model) and uses registry to instantiate adapters with project-specific credentials.
4. **Project Configuration Structure**
```json
{
"providers": {
"stripe": {
"secretKey": "...",
"publishableKey": "...",
"webhookSecret": "...",
"webhookEndpointId": "...",
"currency": "usd",
"defaultTaxRates": [],
"capabilities": {
"meteredBilling": true,
"checkout": true
}
},
"ultra": {
"apiKey": "...",
"region": "us-east",
"capabilities": { ... }
}
},
"defaults": {
"currency": "usd",
"trialDays": 14
}
}
```
- Stored under `projects.payments` attribute (JSON, with `encrypt` filter for secrets). Project update endpoints manage serialization.
5. **Adapter Lifecycle**
- **Configure**: Called when admin enables provider. Adapter sets up webhook endpoints or necessary products where required. Returns sanitized state (IDs, statuses).
- **Plan Management**: Core module persists generic plan data; adapter ensures provider artifacts (product IDs, price IDs) exist and stores references in plan document.
- **Subscription Flow**: Module constructs canonical subscription request, adapter handles vendor-specific operations (creating customers, scheduling invoices), returns provider subscription key stored alongside Appwrite subscription record.
- **Usage Reporting**: Module records usage in internal ledger immediately; adapter may asynchronously forward to provider (e.g., Stripe metered billing). Worker jobs or sync endpoints ensure eventual consistency.
- **Webhooks**: Module registers provider-specific webhook endpoints; incoming webhooks use registry to locate appropriate adapter and update `payments_subscriptions` or usage data.
6. **Error Handling**
- Create `src/Appwrite/Payments/Exception/PaymentException.php` with standardized codes (e.g., `PROVIDER_AUTH_FAILED`, `PLAN_CONFLICT`, `USAGE_REPORT_FAILED`).
- Adapter implementations throw provider-specific exceptions mapped to `PaymentException` with actionable messages.
### 4. Database Schema
Create new configuration file `app/config/collections/payments.php`. Proposed collections:
1. **`payments_plans`**
- `projectId`, `projectInternalId` (key indexes).
- `planId` (custom ID exposed to clients).
- `name`, `description`, `pricing` array (support multiple billing intervals/currencies), `isDefault`, `isFree`, `status`, `migrationVersion`.
- `providers` JSON: map provider identifier -> { productId, priceIds, metadata }.
- `features` summary JSON for quick lookup.
- Indexes: unique (`projectId`, `planId`), key on `status`, fulltext `search` combining name/description.
2. **`payments_features`**
- Definition list available for assignment to plans.
- Attributes: `featureId`, `name`, `type`, `description`, `defaultIncludedUnits`, `defaultTiers`, etc.
- `providers` JSON storing mapping to external meter IDs or feature codes.
- Indexes: unique (`projectId`, `featureId`), key on `type`.
3. **`payments_plan_features`**
- Join table linking plans to feature configs.
- Attributes: `planId`, `featureId`, `type`, `enabled`, `currency`, `interval`, `includedUnits`, `tiersMode`, `tiers`, `usageCap`, `overagePrice`, `providers` JSON (price/meter references), `metadata` JSON.
- Indexes: unique (`projectId`, `planId`, `featureId`), key on `type`, `active` flag.
4. **`payments_subscriptions`**
- Tracks active/past subscriptions.
- Attributes: `subscriptionId` (Appwrite ID), `projectId`, `actorType` (`user`/`team`), `actorId`, `actorInternalId`, `planId`, `status`, `trialEndsAt`, `currentPeriodStart`, `currentPeriodEnd`, `cancelAtPeriodEnd`, `canceledAt`, `providerStatus`, `providers` JSON (customerId, subscriptionId, invoiceId, lastSyncedAt), `usageSummary` JSON, `tags` array, `search` field.
- Indexes: key on (`projectId`, `actorType`, `actorId`), key on `status`, key on `planId`, fulltext search.
5. **`payments_usage_events`**
- Internal usage ledger.
- Attributes: `projectId`, `subscriptionId`, `actorType`, `actorId`, `planId`, `featureId`, `quantity`, `timestamp`, `providerSyncState`, `providerEventId`, `metadata`.
- Indexes: key on (`projectId`, `subscriptionId`, `featureId`, `timestamp`), key on `providerSyncState`.
6. **`payments_provider_logs`** (optional) – store provider interactions/webhook payloads for debugging.
**Migration Strategy**
- Update `app/config/collections.php` to load `payments.php` similar to other modules.
### 5. Business Logic Flow
1. **Plan Creation**
- Validate request (IDs, pricing structure) using new validators (`Payments\Validator\PlanPayload`).
- Persist plan document in `payments_plans` (without provider references initially).
- For each enabled provider, call adapter->ensurePlan. Merge returned references into `providers` JSON and persist.
- Emit events/audits (`payments.plans.[planId].create`).
2. **Feature Assignment**
- Validate feature definitions exist and types are allowed.
- Persist in `payments_plan_features`, call adapter->ensureFeature where required (e.g., create metered price). Update plan document summary.
3. **Subscription Lifecycle**
- `POST /subscriptions`: Determine actor document (fetch user or team). If team, ensure membership + permissions.
- Check existing active subscription; depending on config, allow multiple or enforce single.
- For free plans: create internal subscription record without provider interaction.
- For paid plans: adapter->ensureSubscription (create customer if needed, attach payment method, start subscription). Store provider subscription ID and status.
- Update `payments_subscriptions` with internal status (`active`, `trialing`, `past_due`, etc.).
- Trigger usage initialization jobs and optionally send welcome events.
4. **Usage Tracking**
- Services emit usage via `POST /subscriptions/:id/usage` (server key or internal worker). Each entry stored in `payments_usage_events` and aggregated for quick reads.
- Cron/worker picks unsynced events, groups by provider, calls adapter->reportUsage or other relevant API.
- `GET /subscriptions/:id/usage` returns aggregated totals (internal vs provider synced) plus detail entries.
5. **Provider Webhooks**
- Webhook endpoint verifies signature (adapter->handleWebhook). Update subscription statuses (e.g., `past_due`, `canceled`), record invoice IDs, mark usage sync states.
- Fire events to notify console or automation flows.
6. **Project Configuration**
- `PUT /payments/providers`: Accept provider configs, run validations (fields required per provider). Adapter->configure may create webhook endpoints, check API keys. Persist sanitized config in `project.payments` JSON and update `projects` document.
- Provide GET endpoint returning provider status for console (enabled, last sync, warnings).
### 6. Security & Permissions
- Scope mapping:
- `payments.read`: Admins or service keys retrieving plan/subscription data.
- `payments.write`: Admin actions to manage plans, providers, migrations.
- `payments.subscribe`: Actors initiating subscription changes (users/teams). Distinguish between user session vs team owner in access control.
- Authorization logic uses existing role helpers (e.g., `Role::user`, `Role::team`). Ensure team endpoints check membership + `owner` or `billing` roles.
- Sensitive provider credentials stored encrypted; route responses never expose secrets.
- Webhook endpoints use provider-specific secret validation; avoid exposing under public documentation.
### 7. Testing & QA
- **Unit Tests:**
- Provider adapter contract (mock HTTP clients) verifying plan creation, subscription lifecycle, error propagation.
- Validators for plan payloads, provider configs.
- **E2E Tests:**
- Add `tests/e2e/Services/Payments/` with scenarios: plan CRUD, subscription flow (user + team), usage reporting, provider configuration failure.
- Use mock provider adapter for deterministic responses.
- **Integration Tests:**
- Worker tests simulating usage reconciliation.
- Migration tests verifying data transformation from legacy schemas.
- **Benchmarks:**
- Evaluate overhead of usage events and plan operations under load to adjust indexes.
### 8. Migration & Rollout
1. **Phase Gate**
- Ship module behind feature flag (`project.payments.enabled`). Default disabled until admin configures provider.
- Provide CLI to enable module per project post-migration.
2. **Data Migration**
- CLI script reads existing `auth_*` collections and populates new schema.
- Migrate user-level `planId`, `stripeCustomerId`, etc., into `payments_subscriptions` records with `actorType=user`.
- Generate default team subscriptions if teams had equivalent data (if none, start blank).
3. **Documentation & Console Update**
- Update console to consume `/v1/payments` endpoints (plan management UI, subscription views, usage dashboards).
- Provide developer docs describing multi-actor support, provider configuration, usage APIs, error handling.
### 9. Open Issues & Decisions Needed
- **Team billing semantics**: define allowed team roles for subscription management, and how consumption is aggregated across team members.
- **Multiple providers simultaneously**: Determine whether a project can use multiple providers concurrently (failover vs multi-market). Data model supports multiple by design; confirm product direction.
- **Self-hosted provider**: Consider built-in “Manual” provider for on-prem setups wanting internal invoicing without external gateway.
- **Plan versioning**: Decide if plan updates create new versions or mutate in place; consider locking for existing subscriptions.
- **Grace periods**: Align with Stripe-style grace or immediate suspension rules; ensure configurable per provider/project.
### 10. Next Steps
1. Validate blueprint with stakeholders (engineering leads, product).
2. Break implementation into milestones: module skeleton → data schema → provider abstraction → endpoints → migrations/tests.
3. Allocate time for console integration and documentation once backend stabilizes.
-350
View File
@@ -1,350 +0,0 @@
# Payments Revamp Stepbook
This stepbook sequences the end-to-end implementation of the payments revamp. Each step is agent-friendly, with explicit prerequisites, work instructions, deliverables, and verification guidance. Follow the steps in order unless a dependency explicitly states otherwise.
## Legend
- **Prereqs**: Conditions or artifacts required before tackling the step.
- **Core Tasks**: Ordered instructions for human/AI agents.
- **Outputs**: Expected tangible results (code, docs, configs).
- **Verification**: Commands or checks that confirm completion.
- **Handoff**: Follow-up steps or consumers of the outputs.
---
## Step 1 – Module Scaffolding
- **Prereqs**
- Workspace prep complete.
- **Core Tasks**
1. Create module namespace: `src/Appwrite/Platform/Modules/Payments/`.
2. Add `Module.php` mirroring structure of `Functions\Module`.
3. Create `Services/Http.php` (and placeholder `Services/Workers.php` if background work planned) extending `Utopia\Platform\Service`.
4. Register module within `src/Appwrite/Platform/Appwrite.php` constructor via `$this->addModule(new Payments\Module());`.
5. Ensure Composer autoload already covers `src/Appwrite` (confirm `composer.json` PSR-4 mapping). If gaps exist, patch accordingly.
- **Outputs**
- Module skeleton files committed with minimal placeholder logic.
- **Verification**
- `composer dump-autoload` executes without errors.
- `vendor/bin/phpunit --filter Payments` (should report zero tests but no failures).
- `php -l` on new files passes.
- **Handoff**
- Step 4 uses module skeleton to register HTTP actions.
## Step 4 – HTTP Route Class Framework
- **Prereqs**
- Module skeleton registered; app boots without fatal errors.
- **Core Tasks**
1. Design folder structure under `src/Appwrite/Platform/Modules/Payments/Http/` matching endpoint domains (e.g., `Plans`, `Features`, `PlanFeatures`, `Subscriptions`, `Usage`, `Providers`, `Webhooks`).
2. For each endpoint group, create base action classes extending `Appwrite\Platform\Modules\Compute\Base` (or appropriate lightweight variant) and `use HTTP` trait.
3. In `Services/Http.php`, call `addAction(new Plans\Create())`, etc., for every planned route.
4. Stub methods: set `setName`, `setHttpMethod`, `setHttpPath`, `setDescription`, `setGroup`, placeholder `->param` definitions.
5. Add TODO comments referencing Step 8/9 tasks for actual business logic.
- **Outputs**
- Skeleton route classes with metadata scaffolding.
- **Verification**
- `php -l` on all HTTP files.
- Application boot (via `php -d detect_unicode=0 app/init.php`) logs no missing class errors.
- **Handoff**
- Step 5 introduces scope/SDK metadata consumed by these classes.
## Step 5 – Scope & SDK Metadata Wiring
- **Prereqs**
- Route classes stubbed.
- **Core Tasks**
1. Update `app/config/scopes.php` to define `payments.read`, `payments.write`, `payments.subscribe` (client/server/console mappings per blueprint).
2. Add console/admin scope entries and ensure scope descriptions reflect usage.
3. In each route class, register SDK metadata via `$this->label('sdk', new Method(...))` specifying group `payments`, auth types, and method name.
4. Annotate routes with `->label('scope', 'payments.read')` etc.; include `resourceType`, `event`, and `audits.*` labels where applicable.
5. Update scope tests under `tests/e2e/Scopes` (add cases ensuring new scopes exist and map to correct roles).
6. Regenerate scope documentation if automation expects (`bin/specs scopes`).
- **Outputs**
- Scopes defined and linked; routes enriched with metadata.
- **Verification**
- `vendor/bin/phpunit tests/e2e/Scopes` passes (or targeted test command).
- `bin/specs` (or relevant generator) succeeds without missing scope errors.
- **Handoff**
- Step 6 relies on new scopes for permission configuration.
## Step 6 – Data Model Definition
- **Prereqs**
- Scope wiring complete; DBA consulted for index strategy (if required).
- **Core Tasks**
1. Create `app/config/collections/payments.php` describing all new collections:
- `payments_plans`
- `payments_features`
- `payments_plan_features`
- `payments_subscriptions`
- `payments_usage_events`
- optional `payments_provider_logs`
2. For each collection, define attributes (types, filters, required flags) exactly as blueprint specifies (IDs, JSON fields, indexes, encryption flags).
3. Update `app/config/collections.php` to include the new config file.
4. Remove/flag legacy Stripe-specific attributes from `app/config/collections/platform.php` and `app/config/collections/common.php`; add `projects.payments` JSON attribute with `encrypt` filter for secrets.
5. Draft migration notes describing how new collections replace old fields (feeds into Step 11).
- **Outputs**
- Payments collections configuration file with indexes and attribute metadata.
- Legacy collection definitions cleaned up.
- **Verification**
- Run `php -d detect_unicode=0 app/init.php` to ensure config loads.
- `vendor/bin/phpunit tests/unit/Database/Collections` (or relevant) passes.
- **Handoff**
- Step 7 implements provider abstractions referencing new schema.
## Step 7 – Provider Abstraction Layer
- **Prereqs**
- Data model defined; requirements for provider interactions clarified.
- **Core Tasks**
1. Create namespace `src/Appwrite/Payments/Provider/`.
2. Implement `Adapter` interface (or `ProviderAdapterInterface`) as per blueprint signature (plan management, subscriptions, usage, webhooks, testing).
3. Define supporting value objects (`ProviderState`, `ProviderPlanRef`, `ProviderFeatureRef`, `ProviderSubscriptionRef`, `ProviderCheckoutSession`, `ProviderPortalSession`, `ProviderUsageReport`, `ProviderWebhookResult`, `ProviderTestResult`).
4. Build `Registry` class to resolve adapters by identifier, instantiate with project-specific config, and cache where appropriate.
5. Migrate existing Stripe logic (`src/Appwrite/Auth/Subscription/StripeService.php`) into `Payments\Provider\StripeAdapter` implementing interface.
6. Relocate validators and exceptions into `src/Appwrite/Payments/Validator` and `src/Appwrite/Payments/Exception\PaymentException.php`.
7. Ensure adapter error handling maps provider-specific issues to standardized `PaymentException` codes (`PROVIDER_AUTH_FAILED`, `PLAN_CONFLICT`, etc.).
- **Outputs**
- Provider interface and registry.
- Stripe adapter aligned with new abstractions.
- **Verification**
- Unit tests for registry and adapter contract (`tests/unit/Payments/Provider/`) passing.
- Static analysis (`vendor/bin/phpstan analyse src/Appwrite/Payments`).
- **Handoff**
- Step 8 integrates registry into route actions.
## Step 8 – Business Logic Integration (Plans & Features)
- **Prereqs**
- Provider layer functional; data model available.
- **Core Tasks**
1. Implement `Plans` routes (`Create`, `Get`, `List`, `Update`, `Delete`) to validate payloads (`Payments\Validator\PlanPayload`), persist documents in `payments_plans`, and invoke provider adapters for provisioning.
2. Build `Features` routes for reusable feature definitions, including create/update/delete flows with provider interactions (`ensureFeature`).
3. Implement `PlanFeatures` routes (`Assign`, `List`, `Remove`) to link plans and features, manage tiered pricing, and update plan summaries.
4. Ensure events/audits triggered (`payments.plans.{planId}.create`, etc.).
5. Write unit tests for validators and repository interactions; add e2e tests covering positive/negative plan scenarios.
- **Outputs**
- Fully functioning plan and feature endpoints with provider provisioning.
- **Verification**
- `vendor/bin/phpunit tests/unit/Payments/Plans` and `tests/e2e/Services/Payments/Plans`.
- Manual API smoke tests via `docs/scripts` or HTTP client verifying CRUD operations.
- **Handoff**
- Step 9 builds subscription lifecycle using same abstractions.
## Step 9 – Business Logic Integration (Subscriptions & Usage)
- **Prereqs**
- Plans/features endpoints operational; provider adapter tested.
- **Core Tasks**
1. Implement subscription routes:
- `POST /v1/payments/subscriptions`
- `GET /v1/payments/subscriptions`
- `GET /v1/payments/subscriptions/:subscriptionId`
- `PATCH /v1/payments/subscriptions/:subscriptionId`
- `POST /v1/payments/subscriptions/:subscriptionId/cancel`
- `POST /v1/payments/subscriptions/:subscriptionId/resume`
2. Logic should:
- Resolve actor documents (user/team) with permission checks.
- Handle free vs paid plan flows (internal-only vs provider interactions).
- Persist subscription state in `payments_subscriptions` with lifecycle timestamps.
- Trigger events/audits and queue initialization jobs.
3. Implement usage endpoints:
- `GET /v1/payments/subscriptions/:subscriptionId/usage`
- `POST /v1/payments/subscriptions/:subscriptionId/usage`
- `GET /v1/payments/usage/events`
- `POST /v1/payments/usage/reconcile`
4. Wire usage reporting to adapters (`reportUsage`, `syncUsage`) and internal ledger (`payments_usage_events`).
5. Add permission checks for `payments.subscribe` vs `payments.write`; ensure team role enforcement (`owner`, `billing`).
6. Unit + e2e tests covering actor scenarios, cancellations, resumes, usage reporting, reconciliation.
- **Outputs**
- Subscription and usage endpoints with provider interoperability.
- **Verification**
- `vendor/bin/phpunit tests/unit/Payments/Subscriptions` & `Usage` suites.
- `tests/e2e/Services/Payments/Subscriptions` scenarios.
- Manual smoke test (create plan, assign feature, create subscription, report usage, reconcile).
- **Handoff**
- Step 10 handles provider configuration endpoints and webhooks.
## Step 10 – Provider Configuration & Webhooks
- **Prereqs**
- Adapter registry in place; subscription flows implemented.
- **Core Tasks**
1. Build provider management routes:
- `GET /v1/payments/providers`
- `PUT /v1/payments/providers`
- `POST /v1/payments/providers/:providerId/actions/test`
2. Persist provider configuration in `projects.payments` JSON (encrypted fields) with validation per provider.
3. Implement webhook handler `POST /v1/payments/webhooks/:providerId` with signature validation delegating to adapter `handleWebhook`.
4. Ensure webhook updates subscription status, invoices, usage sync states, and emits relevant events.
5. Add unit tests for configuration validators and webhook processing; e2e tests simulating webhook payloads (mock provider).
- **Outputs**
- Provider configuration endpoints and webhook infrastructure.
- **Verification**
- `vendor/bin/phpunit tests/unit/Payments/Providers`.
- `tests/e2e/Services/Payments/Providers` including webhook scenarios.
- **Handoff**
- Step 11 introduces background workers for async tasks.
## Step 11 – Background Workers & Queues
- **Prereqs**
- Provider webhooks implemented; need for async operations confirmed.
- **Core Tasks**
1. If required, implement `Services/Workers.php` under payments module registering worker actions (usage sync, webhook replay, reconciliation, notifications).
2. Connect workers to relevant queues (`queueForEvents`, `queueForStatsUsage`, etc.).
3. Implement worker handlers for:
- Processing unsynced usage events in batches.
- Retrying failed provider interactions.
- Emitting audit logs/notifications.
4. Add configuration toggles/cron definitions if periodic jobs required.
5. Write worker unit/integration tests (simulate queue payloads, ensure idempotency) and update CI to execute them.
- **Outputs**
- Worker services handling async tasks with tests.
- **Verification**
- `vendor/bin/phpunit tests/unit/Payments/Workers`.
- Manual queue dry-run via `php bin/queue --capture payments-usage-sync` (or equivalent) shows successful execution.
- **Handoff**
- Step 12 manages migrations and data backfill.
## Step 12 – Data Migration & Compatibility Layer
- **Prereqs**
- Core functionality ready; legacy schema inventoried.
- **Core Tasks**
1. Develop migration CLI (e.g., `src/Appwrite/Platform/Tasks/MigratePayments.php`) to read legacy `auth_*` collections and user fields, populate new `payments_*` collections.
2. Handle user-level Stripe data conversion into subscriptions (`actorType=user`) and generate team records as needed.
3. Provide dry-run mode producing migration report.
4. Implement compatibility wrappers for legacy `/v1/account/subscription*` endpoints (if transitional period required) calling new module while preserving response shape.
5. Document manual fallback steps (disable module, rollback) and update upgrade scripts (`bin/upgrade`, `bin/migrate`).
- **Outputs**
- Migration CLI + documentation.
- Optional compatibility endpoints bridging legacy clients.
- **Verification**
- `php bin/migrate payments --dry-run` (or equivalent) generates expected report.
- Integration test seeding legacy data and validating converted records.
- **Handoff**
- Step 13 regenerates specifications and SDKs.
## Step 13 – Specification & SDK Updates
- **Prereqs**
- API routes finalized; migration plan approved.
- **Core Tasks**
1. Update OpenAPI spec generation tooling (`bin/specs`, `src/Appwrite/Platform/Tasks/Specs.php`) to include `/v1/payments` endpoints with accurate schemas/models.
2. Create new response models under `src/Appwrite/Utopia/Response/Model` (Plan, PlanList, Feature, Subscription, UsageEvent, etc.) and ensure routes reference them.
3. Regenerate SDKs (`bin/sdks generate all`) and verify new payments namespace appears across client/server SDKs.
4. Audit console web client to ensure TypeScript models integrate new endpoints (update `public/sdk-console` if required).
- **Outputs**
- Updated specs, generated SDK artifacts, and console types.
- **Verification**
- `git diff` shows updated spec JSON + SDK source.
- Run sample SDK script invoking payments endpoints successfully (smoke test).
- **Handoff**
- Step 14 focuses on testing strategy.
## Step 14 – Testing & Quality Assurance
- **Prereqs**
- Functional code complete; tests implemented alongside earlier steps.
- **Core Tasks**
1. Ensure unit, e2e, integration, and worker tests cover:
- Plan/feature CRUD
- Subscription lifecycle (user/team)
- Usage reporting & reconciliation
- Provider configuration + webhooks
- Migration scripts
2. Add benchmark tests if usage events need performance validation.
3. Execute full test matrix: `vendor/bin/phpunit`, `npm run test` (if console updated), `bin/bench` (if available).
4. Collect coverage report and ensure thresholds met (update `phpunit.xml` if needed).
5. Document manual QA scenarios (console UI walk-through, webhook replay).
- **Outputs**
- Comprehensive passing test suite and QA checklist.
- **Verification**
- CI pipeline green across all stages.
- QA sign-off recorded in tracker.
- **Handoff**
- Step 15 handles documentation and developer experience.
## Step 15 – Documentation & Developer Experience
- **Prereqs**
- Testing complete; product sign-off on functionality.
- **Core Tasks**
1. Author `docs/services/payments.md` covering configuration, endpoints, usage reporting, migration notes.
2. Update console documentation and copy (billing UI, error messages).
3. Refresh developer tutorials (CLI scripts for plan creation, sample usage reporting).
4. Update CHANGELOG/RELEASE notes summarizing payments revamp.
5. Provide sample automation scripts in `docs/examples` demonstrating typical flows.
- **Outputs**
- Updated documentation stack.
- **Verification**
- Docs build (if applicable) passes (`npm run docs:build` or similar).
- Technical writers/product review completed.
- **Handoff**
- Step 16 manages rollout and feature flagging.
## Step 16 – Rollout & Feature Flag Management
- **Prereqs**
- Documentation ready; QA sign-off.
- **Core Tasks**
1. Implement feature flag gating (`project.payments.enabled`) with default disabled; ensure configuration endpoint toggles it.
2. Prepare rollout plan (beta projects, staged enablement, monitoring dashboards).
3. Update CLI tooling or admin UI to toggle flag per project post-migration.
4. Coordinate with support to craft customer communication, fallback procedures.
5. Monitor metrics (errors, webhook failures, subscription churn) during rollout.
- **Outputs**
- Feature flag controls, rollout schedule, monitoring plan.
- **Verification**
- Dry-run enabling flag on staging succeeds end-to-end.
- Monitoring alerts configured with thresholds.
- **Handoff**
- Step 17 covers legacy deprecation.
## Step 17 – Legacy Endpoint Deprecation
- **Prereqs**
- Rollout underway; compatibility shims deployed.
- **Core Tasks**
1. Announce deprecation timeline for `/v1/account/subscription*` endpoints.
2. Implement logging/metrics to detect lingering usage of legacy endpoints.
3. Provide migration guidance to SDK consumers (release notes, upgrade guides).
4. Schedule removal window; when ready, retire compatibility shims and delete legacy controllers/config.
5. Update specs/SDKs to remove legacy endpoints once EOL date passes.
- **Outputs**
- Legacy endpoints officially deprecated and removed per schedule.
- **Verification**
- No production traffic to legacy routes (dashboards confirm zero hits).
- Codebase free of legacy controllers/paths.
- **Handoff**
- Step 18 finalizes project closure.
## Step 18 – Project Closure & Retrospective
- **Prereqs**
- Legacy endpoints removed; new payments module stable in production.
- **Core Tasks**
1. Conduct retrospective documenting successes, challenges, follow-up actions.
2. Archive decision log updates, migration scripts outcomes, and open issues resolved/deferred.
3. Confirm tracker tasks closed and post-rollout monitoring handed to operations.
4. Celebrate with team acknowledgement (optional but recommended!).
- **Outputs**
- Retrospective report, cleaned tracker, knowledge captured.
- **Verification**
- All project artifacts stored in agreed knowledge base.
- Stakeholders acknowledge completion.
- **Handoff**
- None; project concluded.
---
### Supporting References
- `REVAMP_PLAN_DETAILED.md` – Deep blueprint for architecture, API, adapters, data model.
- `REVAMP_PLAN.md` – High-level roadmap and phase breakdown.
- Existing module patterns: `src/Appwrite/Platform/Modules/Functions`, `Sites`.
- Provider integration precedents: `src/Appwrite/Messaging/Provider`, `src/Appwrite/Auth/Subscription/StripeService.php` (legacy).
Use this stepbook as the authoritative sequencing for agents and humans collaborating on the payments revamp. Update the document if scope adjustments or new decisions arise.
-370
View File
@@ -1,370 +0,0 @@
## Payments Revamp Test Plan (Step-by-step, with sample bodies)
Variables used below:
- BASE: https://your-appwrite-endpoint
- PROJECT_ID: your project ID
- API_KEY: Admin API key with payments.write/read/subscribe
- USER_ID: existing user ID (payer for user subscriptions and team payer)
- TEAM_ID: existing team ID (for team subscriptions)
Common headers (Admin key):
```
-H "X-Appwrite-Project: PROJECT_ID" \
-H "X-Appwrite-Key: API_KEY" \
-H "Content-Type: application/json"
```
### 1) Configure provider (Stripe) and enable payments
Request:
```bash
curl -X PUT "$BASE/v1/payments/providers" \
-H "X-Appwrite-Project: PROJECT_ID" \
-H "X-Appwrite-Key: API_KEY" \
-H "Content-Type: application/json" \
-d '{
"config": {
"enabled": true,
"providers": {
"stripe": {
"secretKey": "sk_test_xxx",
"publishableKey": "pk_test_xxx"
}
}
}
}'
```
Verify (secrets masked):
```bash
curl -X GET "$BASE/v1/payments/providers" \
-H "X-Appwrite-Project: PROJECT_ID" \
-H "X-Appwrite-Key: API_KEY"
```
Optional: test credentials
```bash
curl -X POST "$BASE/v1/payments/providers/stripe/actions/test" \
-H "X-Appwrite-Project: PROJECT_ID" \
-H "X-Appwrite-Key: API_KEY"
```
### 2) Create feature definitions
2a. Boolean feature (e.g., builds):
```bash
curl -X POST "$BASE/v1/payments/features" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{
"featureId": "builds",
"name": "Builds",
"type": "boolean",
"description": "Ability to run builds"
}'
```
2b. Metered feature (e.g., requests):
```bash
curl -X POST "$BASE/v1/payments/features" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{
"featureId": "requests",
"name": "Requests",
"type": "metered",
"description": "Metered API requests"
}'
```
List features:
```bash
curl -X GET "$BASE/v1/payments/features" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
### 3) Create a plan with pricing
```bash
curl -X POST "$BASE/v1/payments/plans" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{
"planId": "pro",
"name": "Pro",
"description": "Pro plan",
"isDefault": false,
"isFree": false,
"pricing": [
{ "amount": 1999, "currency": "usd", "interval": "month" },
{ "amount": 19900, "currency": "usd", "interval": "year" }
]
}'
```
Get plan:
```bash
curl -X GET "$BASE/v1/payments/plans/pro" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
### 4) Assign features to the plan
4a. Assign boolean feature (type inferred from feature):
```bash
curl -X POST "$BASE/v1/payments/plans/pro/features" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{
"featureId": "builds",
"currency": "usd",
"interval": "month",
"includedUnits": 0
}'
```
4b. Assign metered feature (type inferred from feature):
```bash
curl -X POST "$BASE/v1/payments/plans/pro/features" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{
"featureId": "requests",
"currency": "usd",
"interval": "month",
"includedUnits": 10000
}'
```
4c. Tiered (graduated) pricing (note):
- The current route accepts the documented fields: `featureId`, `currency`, `interval`, `includedUnits`. Type is inferred from the feature definition.
- Keys like `tiersMode`, `tiers`, `usageCap`, `overagePrice` are not read by the current implementation and will be ignored if sent.
- Below is a future-facing example body for reference only (not applied by current code):
```bash
curl -X POST "$BASE/v1/payments/plans/pro/features" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{
"featureId": "requests",
"type": "metered",
"currency": "usd",
"interval": "month",
"includedUnits": 10000,
"tiersMode": "graduated",
"tiers": [
{ "up_to": 10000, "unit_amount": 0 },
{ "up_to": 100000, "unit_amount": 10 },
{ "up_to": "inf", "unit_amount": 8 }
],
"usageCap": null,
"overagePrice": null
}'
```
List plan features:
```bash
curl -X GET "$BASE/v1/payments/plans/pro/features" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
### 5) Create subscriptions
5a. User subscription:
```bash
curl -X POST "$BASE/v1/payments/subscriptions" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{
"actorType": "user",
"actorId": "USER_ID",
"planId": "pro"
}'
```
5b. Team subscription (payer must be team member with owner/billing):
```bash
curl -X POST "$BASE/v1/payments/subscriptions" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{
"actorType": "team",
"actorId": "TEAM_ID",
"planId": "pro",
"payerUserId": "USER_ID"
}'
```
List subscriptions (optional filters):
```bash
curl -X GET "$BASE/v1/payments/subscriptions?actorType=user&actorId=USER_ID" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
Get subscription by ID:
```bash
curl -X GET "$BASE/v1/payments/subscriptions/SUBSCRIPTION_ID" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
### 6) Update, cancel, resume
6a. Update plan (switch):
```bash
curl -X PATCH "$BASE/v1/payments/subscriptions/SUBSCRIPTION_ID" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{ "planId": "pro" }'
```
6b. Cancel at period end (explicit cancel endpoint):
```bash
curl -X POST "$BASE/v1/payments/subscriptions/SUBSCRIPTION_ID/cancel" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
Alternative using PATCH (sets cancelAtPeriodEnd):
```bash
curl -X PATCH "$BASE/v1/payments/subscriptions/SUBSCRIPTION_ID" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{ "cancelAtPeriodEnd": true }'
```
6c. Resume:
```bash
curl -X POST "$BASE/v1/payments/subscriptions/SUBSCRIPTION_ID/resume" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
### 7) Report usage (metered features)
Emit usage events (e.g., requests):
```bash
curl -X POST "$BASE/v1/payments/subscriptions/SUBSCRIPTION_ID/usage" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{
"featureId": "requests",
"quantity": 2500
}'
```
Get usage summary:
```bash
curl -X GET "$BASE/v1/payments/subscriptions/SUBSCRIPTION_ID/usage" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
List raw usage events:
```bash
curl -X GET "$BASE/v1/payments/usage/events?subscriptionId=SUBSCRIPTION_ID&featureId=requests" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
Reconcile (aggregate summary + optional provider sync):
```bash
curl -X POST "$BASE/v1/payments/usage/reconcile" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
### 7.1) Optional: Update plan and feature definitions
Update plan metadata/pricing (only send fields you want to change):
```bash
curl -X PUT "$BASE/v1/payments/plans/pro" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{
"name": "Pro Updated",
"description": "Updated desc",
"isDefault": false,
"isFree": false,
"pricing": [ { "amount": 2499, "currency": "usd", "interval": "month" } ]
}'
```
Delete plan:
```bash
curl -X DELETE "$BASE/v1/payments/plans/pro" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
Update feature:
```bash
curl -X PUT "$BASE/v1/payments/features/requests" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{ "name": "Requests (updated)", "description": "Updated" }'
```
Delete feature:
```bash
curl -X DELETE "$BASE/v1/payments/features/requests" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY"
```
### 8) Webhooks (Stripe)
Endpoint:
```
POST $BASE/v1/payments/webhooks/stripe/PROJECT_ID
Headers:
Content-Type: application/json
Stripe-Signature: t=timestamp,v1=signature
Body: (Stripe event payload, e.g., customer.subscription.updated)
```
Example minimal payload (adjust to Stripe test fixtures):
```bash
curl -X POST "$BASE/v1/payments/webhooks/stripe/PROJECT_ID" \
-H "Content-Type: application/json" \
-H "Stripe-Signature: t=1700000000,v1=REPLACE_WITH_VALID_SIGNATURE" \
-d '{
"type": "customer.subscription.updated",
"data": { "object": { "id": "sub_123", "status": "active", "current_period_start": 1700000000, "current_period_end": 1702592000 } }
}'
```
Note: Use the `webhookSecret` returned in providers state to compute a valid Stripe signature for real tests.
### 9) Feature flag toggle (block writes when disabled)
Disable payments and verify write routes return 403:
```bash
curl -X PUT "$BASE/v1/payments/providers" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{ "enabled": false, "providers": {} }'
```
Then try, for example, creating a plan (should fail with 403):
```bash
curl -X POST "$BASE/v1/payments/plans" \
-H "X-Appwrite-Project: PROJECT_ID" -H "X-Appwrite-Key: API_KEY" -H "Content-Type: application/json" \
-d '{ "planId": "test", "name": "Test", "pricing": [] }'
```
### 10) (Optional) Worker & scheduler
- Start usage sync worker: `php app/worker.php payments-usage-sync`
- Periodic scheduler (enqueue usage sync for active projects): `php app/cli.php schedule-payments-usage`