4.1.0
Core Forms 4.1.0 — stable
Five months of beta cooking, condensed into one release: payments on submission, headless / cross-origin REST, six new payment-ready templates, a full admin redesign, and a bootstrap hardening that protects the site during plugin upgrades.
Payments on submission
Five providers, all bundled, all webhook-verified.
- Stripe Checkout — hosted Checkout Sessions, test + live keys side-by-side
- PayPal — Orders API, fixed or donor-driven amounts via
[field]references - Razorpay — UPI / cards / wallets via Razorpay Checkout
- Polar.sh — Standard Webhooks signature verification
- FluentCart — hosted-checkout handoff. Free + Pro, one-time + subscriptions. Address fields prefilled into FluentCart’s checkout (and its tax engine). Order paid → submission paid via
fluent_cart/order_paid_done; refunds round-trip.
New submission statuses (pending_payment, paid, payment_failed, refunded), atomic CAS on the paid-state transition so concurrent webhook retries can’t double-fire the action loop, return-URL signing with 2-hour expiry to stop replay.
Headless / cross-origin REST
External sites — Astro, Next.js, Vue, Svelte, plain HTML — can now render and submit Core Forms over REST. Same validation, captcha, action loop and payment redirect machinery runs server-side; the headless site just submits JSON.
GET /wp-json/core-forms/v1/forms/{id}— schema + rendered HTMLPOST /wp-json/core-forms/v1/forms/{id}/submit— runs the full pipelineGET /wp-json/core-forms/v1/embed.js— drop-in vanilla JS widget- Single API key per site (Settings → Headless), per-form opt-in toggle
- Snippets in three flavours: widget, native fetch, cURL — each pre-filled with your actual submit URL + active key
- New “Keeping the API key private” guidance card with copy-paste server-side proxy templates for Cloudflare Workers / Pages Functions, Next.js App Router, and Astro server endpoints
Six new payment-ready starting points
Each ships with a provider preselected so the form lands one configuration step away from working:
| Template | Provider | Pricing |
|—|—|—|
| One-time payment | Stripe | Fixed $19 |
| Donation | PayPal | Donor-entered via [AMOUNT] |
| INR payment | Razorpay | Fixed ₹999 |
| FluentCart product | FluentCart | Variation-driven, with address fieldset |
| Subscription | FluentCart Pro | Variation-driven, recurring |
| Event ticket | Stripe | Tier select drives [TIER] |
Templates registry gained settings and messages callbacks alongside markup. Every starting point — payment and non-payment — now ships with a context-appropriate success message (“Redirecting to PayPal…”, “Booking received…”) instead of the generic “We will be in touch soon.”
Admin redesign
- Per-form Settings, Messages, Payment, Headless tabs converted to native
<details>/<summary>accordions with state badges (rate-limit count, schedule “window set”, honeypot on/off, FluentCart variation price/type/subscription summary, custom CSS/JS indicators, headless On/Off, configured/not-configured per gateway). - Per-form Submissions list rebuilt: Inbox / Spam / All view tabs with live counts (no more bouncing to global Spam to triage one form), redesigned table with status badges (
New/Read/Replied/Archived/Paid/Pending payment/Refunded/Payment failed/Spam), row stripes for new/spam, per-row actions, relative dates with absolute on hover, varied empty states per view. - All Submissions page rebuilt for WordPress 7.0: toolbar split into a filter card (form picker + date range + Filter + Clear) and a search card. Consistent control heights so taller WP 7.0 native dates don’t stagger the row. Side-fix: filter inputs are now in their own GET form so
form_id/date_from/date_toactually land in the URL — previous version had them inside the bulk-actions POST form so filtering silently did nothing. - WordPress Screen Options dropdown hidden across all Core Forms admin screens.
- Persistent “Akismet spam protection is active” banner removed from form-edit pages — status info doesn’t belong as a banner on screens unrelated to it.
- New
cf_get_form_submissions/cf_count_form_submissionsis_spamargument; newbulk_mark_spamper-form action.
Bootstrap hardening
vendor/autoload.phprequire now guarded withfile_exists()so a mid-extraction race during a WordPress in-place plugin upgrade can’t fatal the site. The upgrader replaces the plugin directory non-atomically; a hard require in that window kills every PHP request includingwp-login.php. Now falls through cleanly and surfaces an admin notice on the next page load if vendor/ is genuinely missing.
Modernisation
- Minimum requirements: PHP 8.1, WordPress 6.4
Schema\FieldandSchema\FormSchemaproperties typed,declare(strict_types=1)- CI matrix runs PHP 8.1 / 8.2 / 8.3
prefers-reduced-motionrespected throughout; logical-property RTL fixes- Fullscreen multi-step forms expose
aria-current="step"+ a polite live region for step changes
New filters + actions
cf_payments_orchestrator, cf_payment_completed, cf_form_default_settings, cf_form_templates. Full upgrade guide at docs/getting-started/upgrade-to-4.1.md.
Upgrading
Drop-in compatible from 4.0.x. The vendor-autoload guard means in-place WP upgrades from 4.0.x → 4.1.0 won’t fatal even on slow filesystems. Database migrations run automatically on activation / via upgrader_process_complete.
Full Changelog: