Payment providers
How restaurant payments are wired through Stripe, PayPal, and manual adapters.
Openfront Restaurant uses adapter-based payment providers. Each PaymentProvider record names the adapter for create, capture, refund, status, payment link, and webhook work. The adapters live in:
features/integrations/payment/stripe.tsfeatures/integrations/payment/paypal.tsfeatures/integrations/payment/manual.ts
The record's function fields must be stripe, paypal, or manual. Manage records at /dashboard/platform/payment-providers.
Providers
Stripe (pp_stripe_stripe)
Needs STRIPE_SECRET_KEY, NEXT_PUBLIC_STRIPE_KEY, and STRIPE_WEBHOOK_SECRET. Used for:
- storefront card checkout with payment intents
- POS card payments (
credit_card,debit_card,apple_pay, andgoogle_paytenders all route to Stripe) - capture, status checks, and refunds
- signed webhooks
PayPal (pp_paypal_paypal)
Needs NEXT_PUBLIC_PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, and PAYPAL_WEBHOOK_ID. Sandbox is used unless NEXT_PUBLIC_PAYPAL_SANDBOX=false. Used for storefront checkout with PayPal approval, capture, refunds, and verified webhooks. The POS payment screen has no PayPal tender.
Manual (pp_system_default)
No credentials. Used for cash in the POS and for storefront orders paid outside the app. Storefront orders using it are created as open with a pending payment. The manual adapter does not support webhooks.
Onboarding creates these three records. Stripe and PayPal count as configured only when their environment variables are set.
How the provider model works
Create or seed the provider record
Onboarding creates Stripe, PayPal, and Manual. You can add or toggle records on the Payment Providers page.
The runtime resolves the adapter
features/keystone/utils/paymentProviderAdapter.ts reads the record and calls the named adapter function.
Payments attach to orders
Each Payment links to a RestaurantOrder and its provider. Payments carry an idempotency key, status, method, tip, split index, and refunded amount.
Webhooks are verified and logged
Providers post to /api/integrations/payment/[providerCode]/webhook, for example /api/integrations/payment/pp_stripe_stripe/webhook. The route passes the raw body and headers to handlePaymentProviderWebhook, which checks the signature through the adapter and stores the event as a PaymentWebhookEvent. Duplicate events are ignored. A verified payment event can also complete a pending storefront checkout.
Payment statuses
pending, processing, authorized, unknown, succeeded, failed, cancelled, refunded, partially_refunded. A check cannot close while any of its payments is processing, authorized, or unknown.
Refunds and reconciliation
refundPaymentneeds an approvedManagerApprovalfrom a different user and an idempotency key. It writes aRefundand a refundReceipt.reconcilePaymentandreconcileRefundask the provider for the current state when a result was unclear.retryVerifiedRefundWebhookEventre-processes a stored, verified refund webhook.capturePaymentandgetPaymentStatusare also exposed.
The refund and webhook recovery panels are in the POS "Checkout recovery" section and on the payment screen.
Provider fields
name,code,isInstalledcreatePaymentFunction,capturePaymentFunction,refundPaymentFunction,getPaymentStatusFunction,generatePaymentLinkFunction,handleWebhookFunctioncredentialsandmetadata
The only webhook route is /api/integrations/payment/[providerCode]/webhook.