# Billing and Payments ## Overview The Fotospiel platform supports multiple payment providers for package purchases: Stripe for one-time and subscription payments, and PayPal for orders and subscriptions. Billing is handled through a freemium model with endcustomer event packages and reseller subscriptions. All payments are processed via API integrations, with webhooks for asynchronous updates. ## Stripe Integration - **One-time Payments**: Use Stripe Checkout for endcustomer event packages. Create PaymentIntent via `StripeController@createPaymentIntent`. - **Subscriptions**: Reseller subscriptions use Stripe Subscriptions API. Webhook handling in `StripeWebhookController@handleWebhook` for events like `invoice.paid`, `customer.subscription.deleted`. - **Configuration**: Keys in `config/services.php` under `stripe`. Sandbox mode based on `APP_ENV`. - **Models**: `PackagePurchase` records all transactions with `provider_id` (Stripe PI ID), `status`, `metadata`. - **Frontend**: PurchaseWizard.tsx handles client-side Stripe Elements for card input and confirmation. ## PayPal Integration - **SDK**: Migrated to PayPal Server SDK v1.0+ (`paypal/paypal-server-sdk`). Uses Builder pattern for requests and Controllers for API calls. - **Orders (One-time Payments)**: Endcustomer event packages via Orders API. `PayPalController@createOrder` uses `OrderRequestBuilder` with `CheckoutPaymentIntent::CAPTURE`, custom_id for metadata (tenant_id, package_id, type). Capture in `@captureOrder` using `OrdersController->captureOrder`. DB creation in `processPurchaseFromOrder`. - **Subscriptions (Recurring Payments)**: Reseller subscriptions via Orders API with StoredPaymentSource for recurring setup (no dedicated SubscriptionsController in SDK). `PayPalController@createSubscription` uses `OrderRequestBuilder` with `StoredPaymentSource` (payment_initiator: CUSTOMER, payment_type: RECURRING, usage: FIRST), custom_id including plan_id. Initial order capture sets up subscription; subsequent billing via PayPal dashboard or webhooks. DB records created on initial capture, with expires_at for annual billing. - **Differences**: One-time: Standard Order with payment_type ONE_TIME (default). Recurring: Order with StoredPaymentSource RECURRING to enable future charges without new approvals. plan_id stored in metadata for reference; no separate subscription ID from SDK. - **Client Setup**: OAuth2 Client Credentials flow. Builder: `PaypalServerSdkClientBuilder::init()->clientCredentialsAuthCredentials(ClientCredentialsAuthCredentialsBuilder::init(client_id, secret))->environment(Environment::SANDBOX/PRODUCTION)->build()`. - **Webhooks**: `PayPalWebhookController@verify` handles events like `PAYMENT.CAPTURE.COMPLETED` (process initial/renewal purchase), `BILLING.SUBSCRIPTION.CANCELLED` or equivalent order events (deactivate package). Simplified signature verification (TODO: Implement `VerifyWebhookSignature`). - **Idempotency**: Check `provider_id` in `PackagePurchase` before creation. Transactions for DB safety. - **Configuration**: Keys in `config/services.php` under `paypal`. Sandbox mode via `paypal.sandbox`. - **Migration Notes**: Replaced old Checkout SDK (`PayPalCheckoutSdk`). Updated imports, requests (e.g., OrdersCreateRequest -> OrderRequestBuilder, Subscriptions -> StoredPaymentSource in Orders). Responses: Use `getStatusCode()` and `getResult()`. Tests mocked new structures. No breaking changes in auth or metadata handling; recurring flows now unified under Orders API. ## Database Models - **PackagePurchase**: Records purchases with `tenant_id`, `package_id`, `provider_id` (Stripe PI/PayPal Order ID), `price`, `type` (endcustomer_event/reseller_subscription), `status` (completed/refunded), `metadata` (JSON with provider details). - **TenantPackage**: Active packages with `tenant_id`, `package_id`, `price`, `purchased_at`, `expires_at`, `active` flag. Updated on purchase/cancellation. - **Constraints**: `type` CHECK (endcustomer_event, reseller_subscription), `price` NOT NULL. ## Flows 1. **Purchase Initiation**: User selects package in PurchaseWizard. For free: direct assignment. Paid: Redirect to provider (Stripe Checkout or PayPal approve link). 2. **Completion**: Provider callback/webhook triggers capture/confirmation. Create `PackagePurchase` and `TenantPackage`. Update tenant `subscription_status` to 'active'. 3. **Cancellation/Refund**: Webhook updates status to 'cancelled', deactivates `TenantPackage`. 4. **Trial**: First reseller subscription gets 14-day trial (`expires_at = now() + 14 days`). ## Error Handling - Validation: Request validation for IDs, consent. - API Errors: Catch exceptions, log, return 400/500 JSON. - Idempotency: Prevent duplicate processing. - Webhook: Verify signature, handle unhandled events with logging. ## Testing - Unit: Mock providers in `PurchaseTest.php` for order creation, capture, webhooks. - Integration: Sandbox keys for end-to-end. Assertions on DB state, responses. - Edge Cases: Failures, idempotency, trials, limits. ## Security & Compliance - GDPR: No PII in logs/metadata beyond necessary (tenant_id anonymous). - Auth: Sanctum tokens for API, CSRF for web. - Webhooks: IP whitelisting (PayPal IPs), signature verification. - Retention: Purchases retained per Privacy policy; update on changes.