Webhooks
Inbound PayPal events FloPay subscribes to and how they map to outbound Flo webhooks.
PayPal Events
When a client provisions a direct PayPal gateway, FloPay subscribes to a fixed set of PayPal webhook events at the gateway level and normalizes them into the existing outbound Flo webhook families.
The outbound contract does not change. Direct PayPal events arrive at your client webhook endpoint as the same subscription.*, item.*, and invoice.* events documented in Events. This page is reference material for what happens upstream — your handler code does not need to branch on gateway. The outbound payload shape is identical regardless of which gateway produced the underlying event.
Subscribed PayPal Events
FloPay subscribes to the following 15 PayPal webhook events. These are configured on the PayPal app you provision during direct PayPal setup.
| PayPal event | Purpose |
|---|---|
CHECKOUT.ORDER.APPROVED | Buyer approved a one-time order in the PayPal flow. |
PAYMENT.CAPTURE.COMPLETED | One-time capture succeeded. |
PAYMENT.CAPTURE.DENIED | One-time capture was denied by PayPal. |
PAYMENT.CAPTURE.PENDING | Capture is pending PayPal review. |
PAYMENT.CAPTURE.REFUNDED | A previous capture was refunded. |
PAYMENT.CAPTURE.REVERSED | A previous capture was reversed (chargeback / dispute). |
BILLING.SUBSCRIPTION.CREATED | Subscription created in PayPal. |
BILLING.SUBSCRIPTION.ACTIVATED | Subscription transitioned to active. |
BILLING.SUBSCRIPTION.UPDATED | Subscription details changed (plan, quantity, etc.). |
BILLING.SUBSCRIPTION.CANCELLED | Subscription was cancelled. |
BILLING.SUBSCRIPTION.SUSPENDED | Subscription was suspended (paused). |
BILLING.SUBSCRIPTION.EXPIRED | Subscription reached its end and was not renewed. |
BILLING.SUBSCRIPTION.PAYMENT.FAILED | A recurring charge failed after PayPal exhausted retries. |
VAULT.PAYMENT-TOKEN.CREATED | A vaulted PayPal payment token was created — enables saved-payment upsells. |
VAULT.PAYMENT-TOKEN.DELETED | A vaulted PayPal payment token was removed. |
Signature Verification
FloPay verifies each delivery by calling PayPal's hosted /v1/notifications/verify-webhook-signature endpoint with the webhook id stored in the gateway record's webhook_signing_secret column. Local certificate-based verification is deferred — every inbound PayPal event hits PayPal's verification endpoint before it is processed.
Inbound → Outbound Mapping
| PayPal event | Outbound Flo event | Notes |
|---|---|---|
CHECKOUT.ORDER.APPROVED | (no outbound) | Internal trigger to capture the order. |
PAYMENT.CAPTURE.COMPLETED | item.purchased, invoice.paid | Emitted on successful one-time capture, including the first cycle of a subscription. |
PAYMENT.CAPTURE.DENIED | invoice.overdue | The invoice stays collectible; PayPal denied this specific capture. |
PAYMENT.CAPTURE.PENDING | (no outbound) | FloPay holds processing until the capture resolves to completed, denied, or reversed. |
PAYMENT.CAPTURE.REFUNDED | item.refunded | One-to-one with refunding the capture id. |
PAYMENT.CAPTURE.REVERSED | item.refunded | Reversal (chargeback / dispute) maps to the same outbound family. |
BILLING.SUBSCRIPTION.CREATED | subscription.created | |
BILLING.SUBSCRIPTION.ACTIVATED | subscription.created or subscription.reactivated | subscription.created on first activation; subscription.reactivated when a previously cancelled subscription is restored. |
BILLING.SUBSCRIPTION.UPDATED | subscription.updated | |
BILLING.SUBSCRIPTION.CANCELLED | subscription.cancelled | |
BILLING.SUBSCRIPTION.SUSPENDED | subscription.paused | |
BILLING.SUBSCRIPTION.EXPIRED | subscription.expired | |
BILLING.SUBSCRIPTION.PAYMENT.FAILED | invoice.overdue | PayPal exhausted its retry window — Flo does not issue further off-session retries. |
VAULT.PAYMENT-TOKEN.CREATED | (no outbound) | Internal: FloPay stores the token on user_payment_method with type='paypal' for upsell reuse. |
VAULT.PAYMENT-TOKEN.DELETED | (no outbound) | Internal: FloPay removes the stored token. |
subscription.resumed is emitted when a previously suspended subscription returns to active. Depending on the originating PayPal flow this can arrive via BILLING.SUBSCRIPTION.ACTIVATED or BILLING.SUBSCRIPTION.UPDATED — FloPay normalizes on its own state transition, not the inbound PayPal event name.
Recurring Billing Behaviour
PayPal — not Flo — drives the recurring billing cycle for direct PayPal subscriptions. Renewals fire PAYMENT.CAPTURE.COMPLETED against the subscription, and FloPay emits subscription.renewed (with the linked invoice.paid).
Off-session retries are PayPal's responsibility. FloPay does not retry a failed PayPal recurring charge — once BILLING.SUBSCRIPTION.PAYMENT.FAILED arrives, PayPal has already exhausted its dunning window.
Related
- Events — full outbound event catalog and examples
- Payloads — outbound field-level schema
- Overview — outbound webhook contract
- Direct PayPal — gateway setup and lifecycle