# Payments setup (Stripe & PayPal)

MetaSoul ERP supports:

| Method | Behavior |
|--------|----------|
| **Stripe** | Live Checkout Session on sales orders, portal invoices, and accounting invoices (when credentials configured) |
| **PayPal** | Orders API + capture on return URL; signed webhook for async completion |
| **Demo (Demo Mode)** | Local confirmation only — **no real charge**. Auto-unpublished when `APP_ENV=production` |
| **Wire / COD / Invoice** | Offline pending — browser success URL does **not** mark paid |
| Other regional gateways (Adyen, Mollie, …) | **Do not advertise** — catalog stubs for implementers only; Stripe/PayPal are the marketed checkout providers | See [PAYMENT_GATEWAYS.md](PAYMENT_GATEWAYS.md) (scaffolding reference) |

## Environment variables

Add to `.env` (also listed in `.env.example`):

```env
STRIPE_KEY=pk_test_...
STRIPE_SECRET=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

PAYPAL_CLIENT_ID=
PAYPAL_CLIENT_SECRET=
PAYPAL_SANDBOX=true
PAYPAL_WEBHOOK_ID=
```

You can also store Stripe/PayPal credentials on the provider record under **Sales → Payment Providers**.

## Payment flows

### Sales orders

1. `POST /my/sales/{order}/pay/{provider}` — creates `PaymentTransaction`, redirects to gateway
2. Return: `GET /my/sales/{order}/payments/success?session_id=...` — server-side verify + settle
3. Webhooks: `POST /webhooks/stripe`, `POST /webhooks/paypal`

### Portal invoices

1. `POST /my/invoices/{invoice}/pay` with `provider_id` — redirects to gateway
2. Return: `GET /my/invoices/{invoice}/payments/success?session_id=...`

### Accounting invoices

1. `POST /my/accounting/moves/{move}/online-pay` with `provider_id`
2. Live providers: redirect to Stripe/PayPal checkout
3. Simulated providers: in-app confirm button (test/demo only)
4. Return: `GET /payment/transactions/{transaction}/return?session_id=...`

## Stripe webhook

1. In Stripe Dashboard → Developers → Webhooks, add endpoint:
   - URL: `https://YOUR_DOMAIN/webhooks/stripe`
   - Events: `checkout.session.completed`, `charge.refunded`, `refund.updated`
2. Copy the signing secret into `STRIPE_WEBHOOK_SECRET`.
3. For local E2E: `stripe listen --forward-to http://localhost/webhooks/stripe` (see [PAYMENT_SANDBOX.md](PAYMENT_SANDBOX.md))
4. CSRF is disabled **only** for webhook paths; signature verification is required.
5. Error responses are generic (`Webhook Error`) — details are logged server-side.

## PayPal webhook (recommended for production)

1. In PayPal Developer → Webhooks, add endpoint:
   - URL: `https://YOUR_DOMAIN/webhooks/paypal`
   - Events: `CHECKOUT.ORDER.APPROVED`, `CHECKOUT.ORDER.COMPLETED`, `PAYMENT.CAPTURE.COMPLETED`, `PAYMENT.CAPTURE.REFUNDED`, `PAYMENT.CAPTURE.REVERSED`
2. Copy the webhook ID into `PAYPAL_WEBHOOK_ID`.
3. Signature verification uses PayPal's `verify-webhook-signature` API.
4. Paid status requires **COMPLETED** capture and amount/currency match.

## Refunds

Admin users can refund completed gateway payments:

```http
POST /payment/transactions/{transaction}/refund
```

Optional body: `amount` (partial), `reason`. Stripe uses `stripe_payment_intent_id`; PayPal uses `paypal_capture_id` stored at settlement. Refund webhooks update `payment_refunds` and transaction state idempotently.

Check sandbox setup: `php artisan payments:sandbox-check`

## Success URL behavior

Success/return URLs **never** trust the browser alone:

- **Stripe** — retrieves session, verifies amount/currency/reference
- **PayPal** — captures/verifies order via API
- **Demo** — allowed only outside production
- **Offline** — stays pending

Settlement (`PaymentSettlementService`) atomically marks the transaction done and registers payment on linked sales invoices or accounting moves.

## Production checklist

- Use Stripe **test** keys on demos; **live** keys only in production
- Set `APP_DEBUG=false`
- Confirm webhook delivery in Stripe / PayPal logs after a test payment
- Set both `STRIPE_WEBHOOK_SECRET` and `PAYPAL_WEBHOOK_ID` in production
- Keep the **Demo (Demo Mode)** provider **unpublished** on production

See also: [PAYMENT_AUDIT_REPORT.md](../PAYMENT_AUDIT_REPORT.md), [PAYMENT_SANDBOX.md](PAYMENT_SANDBOX.md)