subbydocs
Visit Subby Start building

WordPress plugin

Install the Subby Checkout plugin, add a shortcode to any page, and grant access from signed Subby webhooks.

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

  1. Go to Plugins → Add New.
  2. Search for Subby Checkout.
  3. Click Install Now, then Activate.

Manual upload

  1. Download the plugin ZIP from WordPress.org.
  2. Go to Plugins → Add New → Upload Plugin.
  3. 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

  1. Open the Subby dashboard.
  2. Go to Developers → API keys.
  3. Copy the publishable key (pk_test_ while you are testing, pk_live_ in production).
  4. 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

  1. Pages → Add New.
  2. Add the shortcode with your plan ID.
  3. 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 access
  • subscription.past_due / charge.failed — warn the subscriber
  • subscription.paused / subscription.escalated — restrict access
  • subscription.recovered / subscription.resumed — restore access
  • subscription.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_pending in the checkout iframe means the bank has the authorisation, not that the subscription is active. Wait for subscription.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

  1. Confirm the plan ID exists in the same environment as the publishable key.
  2. Check Settings → Subby Checkout for a valid pk_test_ or pk_live_ key.
  3. Open the browser console for origin or script errors.
  4. 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

  1. Confirm the endpoint is https://yoursite.com/wp-json/subby/v1/webhook and publicly reachable.
  2. Check Webhook logs for signature failures.
  3. Log the event type early:
add_action('subby_checkout_webhook_received', function ($event) {
  error_log('Subby webhook: ' . $event['type']);
}, 1);
  1. Enable WP_DEBUG and WP_DEBUG_LOG in wp-config.php while 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