The Subby Checkout plugin embeds subscription checkout on any WordPress page or post. You configure a publishable key once, then drop in a shortcode. The plugin is an enrolment surface for the Subscription OS — not a payment gateway and not a replacement for WooCommerce checkout.
For a custom theme or headless WordPress stack, use @mysubbyapp/checkout instead.
Installation
From WordPress.org
- Go to Plugins → Add New.
- Search for Subby Checkout.
- Click Install Now, then Activate.
Manual upload
- Download the plugin ZIP from WordPress.org.
- Go to Plugins → Add New → Upload Plugin.
- Select the ZIP, install, and activate.
WP-CLI
wp plugin install subby-checkout --activate
Configuration
After activating, open Settings → Subby Checkout.
Add a publishable key
- Open the Subby dashboard.
- Go to Developers → API keys.
- Copy the publishable key (
pk_test_while you are testing,pk_live_in production). - Paste it into the plugin settings and save.
Add your WordPress site URL to the key's domain allow-list. Copy Settings → General → Site Address (URL) exactly, including https://.
Environment
| Mode | Key | Charges |
|---|---|---|
| Test | pk_test_ |
Simulated. Use with the sandbox. |
| Live | pk_live_ |
Real collection through the payment rail. |
Appearance defaults
These apply unless a shortcode overrides them.
| Setting | Options | Default |
|---|---|---|
| Theme | light, dark, auto |
light |
| Accent colour | Hex, for example #0B5FFF |
#0B5FFF |
| Border radius | 0–12 px | 8 |
| Font | inherit or a font family |
inherit |
Advanced
- Debug mode — Write plugin logs to
wp-content/debug.log. - Webhook logging — Keep a recent delivery history in Settings → Subby Checkout → Webhook logs.
- Clear cache — Drop cached session fragments after you change keys or appearance.
Shortcode
[subby_checkout product_id="plan_8ZpR1v"]
product_id is the Subby plan ID from Dashboard → Plans (it starts with plan_).
Attributes
| Attribute | Values | Default |
|---|---|---|
product_id |
Plan ID (plan_…) |
Required |
plan |
monthly, annual |
monthly |
theme |
light, dark, auto |
Plugin setting |
accent_color |
Hex colour | Plugin setting |
button_text |
Any string | Subscribe Now |
success_url |
Page URL | Current page |
If your Subby plan already encodes the billing cycle (a monthly plan versus an annual plan), pass that plan's ID and omit plan.
Examples
Basic checkout:
[subby_checkout product_id="plan_8ZpR1v"]
Dark theme and brand colour:
[subby_checkout product_id="plan_8ZpR1v" theme="dark" accent_color="#FF6B6B"]
Redirect after authorisation:
[subby_checkout product_id="plan_8ZpR1v" success_url="/welcome/" button_text="Subscribe to Premium"]
Create a checkout page
- Pages → Add New.
- Add the shortcode with your plan ID.
- Publish.
Use a different shortcode (and plan ID) on each product page if you sell more than one plan.
Hooks and filters
Actions
subby_checkout_before_render
Fires before the iframe is printed.
add_action('subby_checkout_before_render', function ($attributes) {
error_log('Checkout viewed: ' . $attributes['product_id']);
});
subby_checkout_after_render
Fires after the iframe is printed. Useful for help copy under the form.
add_action('subby_checkout_after_render', function ($attributes) {
echo '<p>Questions? <a href="/faq/">See the FAQ</a>.</p>';
});
subby_checkout_session_created
Fires on the server when a checkout session is created.
add_action('subby_checkout_session_created', function ($session) {
if (!is_user_logged_in()) {
return;
}
update_user_meta(
get_current_user_id(),
'last_checkout_session',
$session['id']
);
});
subby_checkout_webhook_received
Fires after the plugin accepts a signed webhook. Grant or revoke access here — not in JavaScript.
add_action('subby_checkout_webhook_received', function ($event) {
if ($event['type'] !== 'subscription.created') {
return;
}
$subscription = $event['data']['object'];
$user_id = $subscription['metadata']['your_user_id'] ?? 0;
$user = $user_id ? get_user_by('id', $user_id) : false;
if (!$user) {
return;
}
$user->add_role('premium_subscriber');
update_user_meta($user->ID, 'subscription_id', $subscription['id']);
});
Filters
subby_checkout_shortcode_attributes
Change attributes before render.
add_filter('subby_checkout_shortcode_attributes', function ($attributes) {
if (wp_is_mobile()) {
$attributes['theme'] = 'dark';
}
return $attributes;
});
subby_checkout_container_class
add_filter('subby_checkout_container_class', function ($class) {
if (is_page('premium')) {
$class .= ' premium-checkout';
}
return $class;
});
subby_checkout_iframe_attributes
add_filter('subby_checkout_iframe_attributes', function ($attributes) {
$attributes['title'] = 'Subby payment checkout';
return $attributes;
});
subby_checkout_api_request_headers
add_filter('subby_checkout_api_request_headers', function ($headers) {
$headers['X-Site-ID'] = 'my-site-123';
return $headers;
});
Webhooks
Register the plugin endpoint in Dashboard → Developers → Webhooks:
https://yoursite.com/wp-json/subby/v1/webhook
Subscribe at least to:
subscription.created— grant accesssubscription.past_due/charge.failed— warn the subscribersubscription.paused/subscription.escalated— restrict accesssubscription.recovered/subscription.resumed— restore accesssubscription.cancelled— remove access
The plugin verifies Subby-Signature before it fires subby_checkout_webhook_received. Do not process unsigned POST bodies yourself. See Webhooks for the payload shape and subscription lifecycle for states.
mandate_pendingin the checkout iframe means the bank has the authorisation, not that the subscription is active. Wait forsubscription.created.
Grant access on subscription.created
Pass metadata.your_user_id when the plugin creates the checkout session (logged-in WordPress user ID). The webhook payload's data.object is a subscription: customer and plan are Subby IDs, not emails.
add_action('subby_checkout_webhook_received', function ($event) {
if ($event['type'] !== 'subscription.created') {
return;
}
$subscription = $event['data']['object'];
$user_id = $subscription['metadata']['your_user_id'] ?? 0;
$user = $user_id ? get_user_by('id', $user_id) : false;
if (!$user) {
return;
}
update_user_meta($user->ID, 'subscription_id', $subscription['id']);
update_user_meta($user->ID, 'subscription_plan', $subscription['plan']);
$user->add_role('premium_subscriber');
wp_mail(
$user->user_email,
'Your subscription is active',
'Welcome. Your Subby subscription is now active.'
);
});
Failed renewal
add_action('subby_checkout_webhook_received', function ($event) {
if ($event['type'] !== 'subscription.past_due') {
return;
}
$subscription = $event['data']['object'];
$user_id = $subscription['metadata']['your_user_id'] ?? 0;
$user = $user_id ? get_user_by('id', $user_id) : false;
if (!$user) {
return;
}
wp_mail(
$user->user_email,
'We could not collect your renewal',
'Update your payment method so your subscription can continue.'
);
});
Cancelled
add_action('subby_checkout_webhook_received', function ($event) {
if ($event['type'] !== 'subscription.cancelled') {
return;
}
$subscription = $event['data']['object'];
$user_id = $subscription['metadata']['your_user_id'] ?? 0;
$user = $user_id ? get_user_by('id', $user_id) : false;
if (!$user) {
return;
}
$user->remove_role('premium_subscriber');
delete_user_meta($user->ID, 'subscription_id');
});
Inspect deliveries in Dashboard → Developers → Webhooks → Deliveries and in Settings → Subby Checkout → Webhook logs. Use Testing in sandbox to trigger events without real charges.
Troubleshooting
Checkout does not appear
- Confirm the plan ID exists in the same environment as the publishable key.
- Check Settings → Subby Checkout for a valid
pk_test_orpk_live_key. - Open the browser console for origin or script errors.
- Add the WordPress site URL to the publishable key allow-list.
origin is not allowed
Copy Settings → General → Site Address (URL) into the allow-list, save, wait a few minutes, and hard-refresh.
Webhooks arrive but access does not change
- Confirm the endpoint is
https://yoursite.com/wp-json/subby/v1/webhookand publicly reachable. - Check Webhook logs for signature failures.
- Log the event type early:
add_action('subby_checkout_webhook_received', function ($event) {
error_log('Subby webhook: ' . $event['type']);
}, 1);
- Enable
WP_DEBUGandWP_DEBUG_LOGinwp-config.phpwhile you investigate. Turn them off in production.
Theme CSS fights the iframe
Scope overrides to the wrapper. Do not restyle the iframe contents — those come from Subby.
.subby-checkout-wrapper {
width: 100%;
max-width: 600px;
margin: 0 auto;
}
.subby-checkout-wrapper iframe {
border: none;
border-radius: 8px;
}
On small screens:
@media (max-width: 600px) {
.subby-checkout-wrapper {
padding: 0 10px;
}
}
Include a viewport meta tag in the theme if checkout looks zoomed on phones.
Frequently asked questions
Can I sell more than one plan?
Yes. Use a different product_id on each page.
Can subscribers change their payment method in WordPress?
Not from the plugin. Send them through a new checkout session or the subscriber portal.
Does the plugin store payment data?
No. Sessions are created on Subby. The plugin stores your settings, shortcode attributes and webhook handling.
Do I need PCI-DSS because of this plugin?
No. Bank details never touch WordPress. PCI scope sits with the payment rail and Subby checkout.
Can I test without taking money?
Yes. Use a pk_test_ key, sandbox plans, and test payment methods.
Does this replace WooCommerce checkout?
No. Use it on dedicated subscribe pages. WooCommerce remains your catalogue and cart if you already use it.
Where are debug logs?
Enable Debug mode in plugin settings. WordPress writes to /wp-content/debug.log.
Next steps
- @mysubbyapp/checkout — Same checkout, in a custom theme or SPA.
- Webhooks — Event types, signatures and retries.
- Going live — Live keys and launch checklist.