{"id":347218,"date":"2026-07-31T08:35:17","date_gmt":"2026-07-31T08:35:17","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/prototypux-saas-engine\/"},"modified":"2026-07-31T15:35:00","modified_gmt":"2026-07-31T15:35:00","slug":"prototypux-saas-engine","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/prototypux-saas-engine\/","author":23540014,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.1","stable_tag":"1.0.1","tested":"7.0.2","requires":"6.2","requires_php":"8.0","requires_plugins":null,"header_name":"Prototypux SaaS Engine","header_author":"Tiago Ferreira","header_description":"Turns a WordPress site into a monetizable SaaS: maps Stripe subscription state to WordPress permissions \u2014 managed role and publishing quotas.","assets_banners_color":"301d6e","last_updated":"2026-07-31 15:35:00","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":31,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"tiagoferreiradesign","date":"2026-07-31 08:34:42"},"1.0.1":{"tag":"1.0.1","author":"tiagoferreiradesign","date":"2026-07-31 15:35:00"}},"upgrade_notice":{"1.0.1":"<p>Cosmetic fix to the welcome notice. No changes to billing, quotas or stored data.<\/p>","1.0.0":"<p>Initial release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3629667,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3629667,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3629667,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3629667,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.0","1.0.1"],"block_files":[],"assets_screenshots":{"screenshot-1.jpg":{"filename":"screenshot-1.jpg","revision":3630175,"resolution":"1","location":"assets","locale":"","width":1247,"height":627},"screenshot-2.jpg":{"filename":"screenshot-2.jpg","revision":3630175,"resolution":"2","location":"assets","locale":"","width":2336,"height":641},"screenshot-3.jpg":{"filename":"screenshot-3.jpg","revision":3630175,"resolution":"3","location":"assets","locale":"","width":2337,"height":1082},"screenshot-4.jpg":{"filename":"screenshot-4.jpg","revision":3630175,"resolution":"4","location":"assets","locale":"","width":2335,"height":1052},"screenshot-5.jpg":{"filename":"screenshot-5.jpg","revision":3630175,"resolution":"5","location":"assets","locale":"","width":2292,"height":992},"screenshot-6.jpg":{"filename":"screenshot-6.jpg","revision":3630175,"resolution":"6","location":"assets","locale":"","width":1692,"height":315}},"screenshots":{"1":"The <code>[wpse_pricing]<\/code> shortcode renders your plans with a monthly\/yearly toggle, the manual feature list, and a checkout button per plan.","2":"The Plans screen: type, prices, the managed role each plan grants, and which plan new users get on registration.","3":"Creating a paid plan \u2014 the Prices tab appears, with an optional monthly and yearly price that sync to Stripe.","4":"A free plan needs no Stripe at all: pick the role and the quota, and optionally assign it automatically on registration.","5":"Settings: Stripe keys per mode, grace period, which content types are limited and what happens when a quota is reached, plus the page mapping.","6":"Tools: one click reconciles every subscription against Stripe and repairs anything a missed webhook left behind."}},"plugin_section":[],"plugin_tags":[1932,12688,1603,5349,4079],"plugin_category":[41,45,58],"plugin_contributors":[273991],"plugin_business_model":[],"class_list":["post-347218","plugin","type-plugin","status-publish","hentry","plugin_tags-membership","plugin_tags-paywall","plugin_tags-saas","plugin_tags-stripe","plugin_tags-subscriptions","plugin_category-communication","plugin_category-ecommerce","plugin_category-user-management","plugin_contributors-tiagoferreiradesign","plugin_committers-tiagoferreiradesign"],"banners":{"banner":"https:\/\/ps.w.org\/prototypux-saas-engine\/assets\/banner-772x250.png?rev=3629667","banner_2x":"https:\/\/ps.w.org\/prototypux-saas-engine\/assets\/banner-1544x500.png?rev=3629667","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/prototypux-saas-engine\/assets\/icon-128x128.png?rev=3629667","icon_2x":"https:\/\/ps.w.org\/prototypux-saas-engine\/assets\/icon-256x256.png?rev=3629667","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/prototypux-saas-engine\/assets\/screenshot-1.jpg?rev=3630175","caption":"The <code>[wpse_pricing]<\/code> shortcode renders your plans with a monthly\/yearly toggle, the manual feature list, and a checkout button per plan."},{"src":"https:\/\/ps.w.org\/prototypux-saas-engine\/assets\/screenshot-2.jpg?rev=3630175","caption":"The Plans screen: type, prices, the managed role each plan grants, and which plan new users get on registration."},{"src":"https:\/\/ps.w.org\/prototypux-saas-engine\/assets\/screenshot-3.jpg?rev=3630175","caption":"Creating a paid plan \u2014 the Prices tab appears, with an optional monthly and yearly price that sync to Stripe."},{"src":"https:\/\/ps.w.org\/prototypux-saas-engine\/assets\/screenshot-4.jpg?rev=3630175","caption":"A free plan needs no Stripe at all: pick the role and the quota, and optionally assign it automatically on registration."},{"src":"https:\/\/ps.w.org\/prototypux-saas-engine\/assets\/screenshot-5.jpg?rev=3630175","caption":"Settings: Stripe keys per mode, grace period, which content types are limited and what happens when a quota is reached, plus the page mapping."},{"src":"https:\/\/ps.w.org\/prototypux-saas-engine\/assets\/screenshot-6.jpg?rev=3630175","caption":"Tools: one click reconciles every subscription against Stripe and repairs anything a missed webhook left behind."}],"raw_content":"<!--section=description-->\n<p>Prototypux SaaS Engine is a lightweight bridge between <strong>billing and permissions<\/strong>. It translates the state of a Stripe subscription into WordPress entitlements \u2014 a managed role, publishing quotas, and plan-based access \u2014 so you can monetize any WordPress site without gluing together form plugins, Stripe feeds and fragile role-switching code.<\/p>\n\n<p>It is <strong>not<\/strong> a traditional membership plugin. Stripe does what Stripe does best (hosted Checkout, dunning, the Customer Portal, receipts, promotion codes, tax); the plugin owns your plans, subscription state, roles and quotas.<\/p>\n\n<h4>What it does<\/h4>\n\n<ul>\n<li><strong>Plans<\/strong> as a custom post type: type (free\/paid), monthly and\/or yearly price, managed role, publishing quotas, fallback plan, and a manual features list for the pricing table.<\/li>\n<li><strong>Quotas on as many content types as you need<\/strong> \u2014 pick them once in Settings, then give every plan its own limit for each (posts, listings, a custom post type, or all of them).<\/li>\n<li><strong>Freemium without trials<\/strong>: a permanent free plan is assigned automatically on registration (by any form or builder). Upgrades go through Stripe.<\/li>\n<li><strong>Hosted Stripe Checkout &amp; Customer Portal<\/strong> \u2014 cards never touch your site (PCI SAQ-A). No embedded SDK: a thin HTTP client with a pinned API version and signed webhooks.<\/li>\n<li><strong>Activation by webhook only<\/strong> \u2014 access is never granted on the checkout redirect, so a closed browser tab never leaves a customer half-subscribed.<\/li>\n<li><strong>Server-side quota enforcement<\/strong> \u2014 publishing limits are enforced in the REST API and on post insert, not just hidden in the UI. Quota = items published (or scheduled) at once; deleting one frees a slot.<\/li>\n<li><strong>A static checkout URL<\/strong> you can paste into any button, menu or email, plus a Customer Portal link.<\/li>\n<li><strong>Account components<\/strong> as shortcodes: pricing table, plan status, usage bars, portal &amp; checkout buttons, plan\/quota conditionals, and CSS visibility classes for page builders.<\/li>\n<li><strong>Manual reconciliation with Stripe<\/strong> (Tools) \u2014 the one-click repair for a missed webhook.<\/li>\n<li><strong>Automatic past-due banner<\/strong> for the affected user, linking to the Customer Portal.<\/li>\n<\/ul>\n\n<h4>Shortcodes<\/h4>\n\n<ul>\n<li><code>[wpse_pricing]<\/code> \u2014 pricing table with a monthly\/yearly toggle and manual feature lists.<\/li>\n<li><code>[wpse_account]<\/code> \u2014 a pre-assembled account page.<\/li>\n<li><code>[wpse_plan_status]<\/code>, <code>[wpse_usage type=\"post\"]<\/code>, <code>[wpse_portal_button]<\/code>, <code>[wpse_checkout_button plan=\"basic\" interval=\"month\"]<\/code><\/li>\n<li><code>[wpse_if_plan plan=\"pro,basic\"]\u2026[\/wpse_if_plan]<\/code> (add <code>not=\"1\"<\/code> to invert)<\/li>\n<li><code>[wpse_can_create type=\"post\"]\u2026[\/wpse_can_create]<\/code> and its opposite <code>[wpse_quota_reached type=\"post\"]\u2026[\/wpse_quota_reached]<\/code><\/li>\n<li><code>[wpse_quota_remaining type=\"post\"]<\/code> and <code>[wpse_quota_message type=\"post\"]<\/code><\/li>\n<\/ul>\n\n<h4>Body CSS classes for builders<\/h4>\n\n<p>For every content type a plan limits, the plugin exposes ready-made classes you can drop onto any element in your page builder:<\/p>\n\n<ul>\n<li><code>wpse-hide-if-quota-full-{type}<\/code> \u2014 hides the element once the quota is full.<\/li>\n<li><code>wpse-show-if-quota-full-{type}<\/code> \u2014 shows the element only when the quota is full.<\/li>\n<\/ul>\n\n<p>Plus <code>wpse-plan-{slug}<\/code>, <code>wpse-status-{status}<\/code>, <code>wpse-can-create-{type}<\/code> \/ <code>wpse-quota-full-{type}<\/code> on the body for custom CSS.<\/p>\n\n<h4>Developer API<\/h4>\n\n<p>Public functions (<code>wpse_subscription()<\/code>, <code>wpse_has_plan()<\/code>, <code>wpse_quota()<\/code>, <code>wpse_can_create()<\/code>, <code>wpse_checkout_url()<\/code>), actions (<code>wpse_activated<\/code>, <code>wpse_status_changed<\/code>, <code>wpse_payment_completed<\/code>, <code>wpse_downgraded<\/code>, <code>wpse_quota_reached<\/code>, <code>wpse_quota_demoted<\/code>) and filters let you extend everything from your own theme or plugin.<\/p>\n\n<h3>External services<\/h3>\n\n<p>This plugin connects to the <strong>Stripe API<\/strong> (<code>https:\/\/api.stripe.com<\/code>) to create and manage subscriptions. Stripe is the payment processor; without it the plugin cannot sell or manage paid plans. No other external service is contacted, and the plugin loads no scripts, styles or fonts from third-party servers.<\/p>\n\n<p>Data is sent to Stripe only in these situations:<\/p>\n\n<ul>\n<li><strong>When an administrator syncs a plan<\/strong> (Plans \u2192 Sync with Stripe): the plan name, price amounts, currency and billing interval. No personal data.<\/li>\n<li><strong>When a logged-in user starts a checkout<\/strong>: their email address, display name, WordPress user ID, the plan ID and your site URL, so Stripe can create the customer record and the plugin can match the resulting subscription back to the right user.<\/li>\n<li><strong>When a logged-in user opens the Customer Portal<\/strong>: their Stripe customer ID.<\/li>\n<li><strong>When an administrator runs Tools \u2192 Reconcile<\/strong>: the plugin requests subscription and invoice data from Stripe for the subscriptions stored on your site.<\/li>\n<\/ul>\n\n<p>Stripe also sends data <strong>to<\/strong> your site through the webhook endpoint (<code>\/wp-json\/wpse\/v1\/webhook<\/code>): subscription and invoice events, whose signature is verified before being processed. Received events are stored in a database table for auditing and are deleted automatically after a configurable retention period (90 days by default).<\/p>\n\n<p>The service is provided by Stripe, Inc.:<\/p>\n\n<ul>\n<li>Terms of Service: https:\/\/stripe.com\/legal\/ssa<\/li>\n<li>Privacy Policy: https:\/\/stripe.com\/privacy<\/li>\n<\/ul>\n\n<p>You need your own Stripe account and API keys, and you are responsible for informing your users, in your own privacy policy, that payment data is processed by Stripe.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin to <code>\/wp-content\/plugins\/<\/code> and activate it.<\/li>\n<li>Go to <strong>SaaS Engine \u2192 Settings<\/strong> and paste your Stripe <strong>test<\/strong> keys (secret, publishable).<\/li>\n<li>Create a webhook endpoint in your Stripe dashboard pointing to the URL shown on the Settings screen, subscribe it to the five listed events, and paste the signing secret.<\/li>\n<li>Create your plans under <strong>SaaS Engine \u2192 Plans<\/strong> (mark one free plan as the registration default).<\/li>\n<li>Add <code>[wpse_pricing]<\/code> to a page. Switch Settings to <strong>live<\/strong> mode when you are ready to charge real cards.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20it%20work%20with%20my%20form%20%2F%20page%20builder%3F\"><h3>Does it work with my form \/ page builder?<\/h3><\/dt>\n<dd><p>Yes. Registration is the site's responsibility \u2014 any form that creates a user triggers the default free plan. Quota enforcement is server-side, so it protects you no matter how content is created. For builders, the plugin outputs plan and quota CSS classes on the <code>body<\/code> element, so you can show or hide any element without writing code.<\/p><\/dd>\n<dt id=\"do%20i%20need%20the%20stripe%20php%20sdk%3F\"><h3>Do I need the Stripe PHP SDK?<\/h3><\/dt>\n<dd><p>No. The plugin talks to the Stripe API directly over the WordPress HTTP API, with a pinned API version and HMAC-verified webhooks. This avoids the fatal conflicts that embedded SDKs cause with other Stripe-using plugins.<\/p><\/dd>\n<dt id=\"a%20customer%20paid%20but%20their%20access%20did%20not%20change.%20what%20now%3F\"><h3>A customer paid but their access did not change. What now?<\/h3><\/dt>\n<dd><p>Almost always a missed webhook \u2014 check that your Stripe webhook endpoint is subscribed to the five events listed on the Settings screen. To repair it immediately, go to <strong>SaaS Engine \u2192 Tools \u2192 Reconcile now<\/strong>: it checks every subscription against Stripe and fixes the drift with one click.<\/p><\/dd>\n<dt id=\"what%20happens%20on%20a%20failed%20payment%3F\"><h3>What happens on a failed payment?<\/h3><\/dt>\n<dd><p>The subscription enters a grace period (configurable) during which access is kept while Stripe retries. If it is not recovered, a cron job downgrades the user to the plan's fallback.<\/p><\/dd>\n<dt id=\"will%20deleting%20a%20plan%20break%20existing%20subscribers%3F\"><h3>Will deleting a plan break existing subscribers?<\/h3><\/dt>\n<dd><p>No \u2014 the plugin blocks deleting a plan that still has active subscribers and points you to archiving it instead (unsellable, but current subscribers keep their conditions).<\/p><\/dd>\n<dt id=\"does%20the%20plugin%20collect%20or%20track%20any%20data%3F\"><h3>Does the plugin collect or track any data?<\/h3><\/dt>\n<dd><p>No. The plugin has no analytics, no telemetry and no \"phone home\" behaviour. It only contacts Stripe, as described in the \"External services\" section.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Fixed: the welcome notice shown after activation used an outdated plugin name.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial public release: plans, Stripe billing engine (Checkout, Customer Portal, webhooks, reconciliation), server-side publishing quotas, account components, pricing table, and the static checkout URL.<\/li>\n<\/ul>","raw_excerpt":"Turn WordPress into a SaaS: map Stripe subscriptions to user roles and publishing quotas. Hosted Checkout, no SDK, enforcement server-side.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/347218","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=347218"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/tiagoferreiradesign"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=347218"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=347218"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=347218"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=347218"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=347218"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=347218"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}