Payments
Accept Stripe and PayPal payments with server-verified, tamper-proof pricing.
Connecting Stripe
Go to ChargeForms → Global Settings → Payment Settings and add your
Stripe publishable and secret keys (test or live mode). Keys are stored
via the same chargeforms_settings_fields-scoped update mechanism every
other setting uses — a settings save never silently blanks a field it
didn't render.
Connecting PayPal
Add your PayPal REST app's Client ID and Secret in the same Payment Settings screen, and choose Sandbox or Live mode independently of your Stripe configuration — the two gateways don't share credentials or mode.
PayPal's flow is structurally different from Stripe's, not just a
drop-in swap: instead of confirming a charge client-side against a
PaymentIntent, the buyer approves an Order through a PayPal-hosted
button, and the server only actually captures the charge afterward —
capture is the verification step. Before an entry is created,
ChargeForms recomputes the total server-side, confirms it matches what
was actually captured, confirms the order's custom_id round-trips to
this form, and checks the transaction ID hasn't been used on a
previous entry — the same anti-tampering guarantees as Stripe, adapted
to PayPal's own order/capture shape rather than PaymentIntents.
Subscriptions
A Payment Method field can be set to recurring billing (monthly or
yearly, with an optional free trial) to create a real Stripe
subscription on submission — Stripe only today; PayPal subscriptions
aren't implemented yet, though the underlying subscriptions table is
gateway-agnostic by design specifically so that can be added later
without a schema change. Subscription status is kept in sync via Stripe
webhooks and mirrors Stripe's own status set exactly (trialing,
active, past_due, canceled, unpaid, paused, etc.), plus one
local addition — expired — synthesized when a subscription ends
because every payment retry failed, distinguishing that from a
deliberate cancellation, a distinction Stripe's own canceled status
otherwise collapses away.
Payment field types
- Payment Item — a fixed-price line item.
- Custom Amount — visitor-entered amount, with a configurable minimum.
- Payment Method — the card element (Stripe) or approval button (PayPal), depending on which gateway the form uses.
- Payment Summary — a running total of every payment field on the form.
Why the amount can't be tampered with
The total charged is always recalculated server-side from the form's own field configuration at submission time — a fixed Payment Item's price is never read from the request body, and a Custom Amount below its configured minimum is floored to that minimum server-side regardless of what the client sent.
Before an entry is created, ChargeForms also:
- Retrieves the PaymentIntent directly from Stripe's own API and
confirms its status is
succeeded. - Confirms the PaymentIntent's metadata references this form — an intent created for a different form is rejected.
- Checks that this specific PaymentIntent ID hasn't already been used on a previous entry, preventing the same successful payment from being replayed into a second free entry.
Any of these checks failing stops the submission — the entry is never created without a verified, non-replayed, correctly-priced payment behind it.