# PayPal payments and donations

PayPal's buttons for products and donations: PayPal accounts, Venmo (US) and cards through PayPal. It shares
`config/catalogue.json`, the payments ledger, `api/payments/totals` and the admin list with `stripe-payments`, so a
website can offer both. Subscriptions are not included; use `stripe-payments` for those.

Requires the `base` package.

## Install

Use the verified installer in the website container. It installs dependencies, checks checksums, and refuses to replace customer changes:

```sh
python /opt/runalio/catalog_install.py paypal-payments
python /opt/runalio/catalog_install.py --status
```

Review the preview and click Publish to promote backend code and pages. Production data remains separate.

To undo the last installation before publishing: `python /opt/runalio/catalog_install.py --rollback`.

## Settings

The customer enters these in Runalio, Settings → Website modules → this module (never in files or chat):

| Name | Required | Secret | Purpose |
|---|---|---|---|
| `PAYPAL_CLIENT_ID` | yes | no | REST app client ID from developer.paypal.com. |
| `PAYPAL_CLIENT_SECRET` | yes | yes | REST app secret. |
| `PAYPAL_ENV` | yes | no | sandbox for testing, live for real payments. |
| `PAYPAL_WEBHOOK_ID` | no | no | ID of the webhook created for the app; enables refunds and closed-tab captures to be recorded. |

## Add it to pages

Copy `site/paypal.js` into `/workspace/site` and add `<script src="paypal.js" defer></script>`, then:

```html
<div data-runalio-paypal data-product="gift-card-50"></div>
<label>Amount (USD) <input id="gift-amount" type="number" min="5" value="25"></label>
<div data-runalio-paypal data-donation-input="#gift-amount"></div>
<p data-runalio-payment-status role="status"></p>
```

The script loads PayPal's SDK with the client ID from `api/payments/config` and renders the buttons.

## API

| Request | Purpose |
|---|---|
| `POST api/payments/paypal/orders` | `{product_id, quantity}` or `{donation_amount}` (cents), returns `{id}` |
| `POST api/payments/paypal/orders/<id>/capture` | Captures after the buyer approves; records the payment |
| `POST api/payments/paypal/webhook` | PayPal events, verified with PayPal's verification API |

## What the customer needs to do

1. Have a PayPal **Business** account.
2. At developer.paypal.com, Apps & Credentials, **Sandbox**: create an app. In Runalio, Settings → Website modules → PayPal,
   add `PAYPAL_CLIENT_ID`, `PAYPAL_CLIENT_SECRET` and `PAYPAL_ENV` = `sandbox`.
3. In the app, add a webhook: URL `<website address>api/payments/paypal/webhook`, events
   `PAYMENT.CAPTURE.COMPLETED` and `PAYMENT.CAPTURE.REFUNDED`. Add its ID as `PAYPAL_WEBHOOK_ID`.
4. Publish and pay with a sandbox buyer account.
5. Repeat steps 2 and 3 under **Live** and set `PAYPAL_ENV` = `live`.

A paid Runalio package keeps the backend always on.
