FAQ2026-07-29T19:49:38+01:00

Connecting

Are Sandbox and Live really separate, or just a toggle over the same connection?2026-07-29T15:12:47+01:00

Fully separate. Each mode has its own OAuth connection, its own access token, and its own webhook secret, stored under different option keys (wpdd_access_token vs wpdd_access_token_sandbox). The Live/Sandbox toggle in Settings just decides which pair of credentials the plugin reads from — it doesn't share or migrate anything between them. Authorising Sandbox does nothing for Live, and vice versa: you'll connect each one separately, and each needs its own webhook secret pasted into the matching field.

What happens if I lose or change `WPDIRECTDEBIT_SALT`?2026-07-29T15:12:47+01:00

Your stored GoCardless access tokens (and any other encrypted plugin data) become permanently unreadable, and you'll need to reconnect. Here's why: encryption always uses a 32-byte key — either WPDIRECTDEBIT_SALT if you've defined it in wp-config.php, or, if you haven't, a hash of WordPress's own AUTH_KEY. That fallback means encryption works out of the box with zero setup, which is great for sandbox testing, but it also means your credentials are only as stable as AUTH_KEY. If you ever regenerate WordPress's secret keys (a security plugin doing a routine rotation, a fresh salts pull from the WordPress.org secret-key API, restoring wp-config.php from a different environment), and you never defined a dedicated WPDIRECTDEBIT_SALT, the old ciphertext can no longer be decrypted — Crypto::decrypt() just returns an empty string. This is exactly why the plugin nags you to add a dedicated salt before going Live: it decouples your GoCardless credentials from WordPress's own key rotation.

I cloned my live site to a staging domain. Why won’t Live mode reconnect?2026-07-29T15:12:47+01:00

The OAuth handshake for Live mode sends your site's URL (home_url()) to wpdirectdebit.com/, which checks it against a subscriber registry before allowing the flow to proceed — this is how the license/subscription is tied to a specific site. Sandbox connections skip this check entirely, which is why sandbox testing "just works" on any domain, including a fresh staging clone. If you need Live credentials on a staging copy, don't try to OAuth from the staging URL — copy the already-encrypted wpdd_access_token option (and matching WPDIRECTDEBIT_SALT) across from the source site instead, or just work in Sandbox on staging and switch to Live only on the real domain.

Currency

Where does a form’s currency actually come from?2026-07-29T15:12:47+01:00

From Gravity Forms' own "Default Currency" setting (Forms → Settings) at the moment you build the form — that's the pre-filled default in the Build Recurring Payment / Build Payment modal, though you can override it per form to any of the plugin's eight supported currencies (GBP, EUR, USD, AUD, NZD, CAD, SEK, DKK) before saving. It's not controlled by WPDD's own "Default country" setting. That setting only pre-fills the address country field in the GoCardless drop-in at signup — it has no bearing on which currency a form charges in. If your GF default currency isn't one of the eight supported currencies, the builder will warn you and require you to pick a supported one explicitly.

Why doesn’t the Instant Bank Pay / PayTo option show up for a form?2026-07-29T15:12:47+01:00

Two independent gates have to both pass. First, the currency has to support an instant scheme at all — currently GBP and EUR map to "Instant Bank Pay," and AUD maps to "PayTo"; every other supported currency has no instant option. Second — and this is the surprising part — availability also depends on WPDD's own Default country setting in Settings, checked against a hardcoded list of countries where GoCardless currently offers instant payments (GB, AU, DE as of this writing). That's your site's configured country, not the customer's actual bank location. If your default country is set to, say, France, the instant-payment option won't offer itself even though the currency (EUR) would otherwise support it — because GoCardless's IBP/SEPA-Instant rollout for EUR only currently covers Germany.

I set an upfront fee when building the form, and now the per-link fee override doesn’t show. Is that a bug?2026-07-29T15:12:47+01:00

No — it's the intended trade-off, though it's easy to expect the opposite. The "Upfront fee" field on the Create Payment Link panel only appears for forms that have a hidden wpdd_token field wired up, and that hidden field is only added to a form at build time if the form was created without a fixed upfront fee. In other words, the per-link fee override and a form-level fixed fee are mutually exclusive by construction: build the form with no fee baked in if you want the flexibility to set different fees per link later; build it with a fixed fee if you want that fee to be non-negotiable and don't need the override.

Subscription quirks

Why is “day of month” capped at 28, and what happens for the 29th–31st?2026-07-29T15:12:47+01:00

GoCardless's day_of_month subscription parameter only accepts 1–28 — full stop, no month-aware rounding on their end. The plugin's UI enforces the same 1–28 range for that reason; there's no way to ask for "always the 31st" or "last day of the month" through this field. If you need end-of-month billing, you'd need to build that logic yourself against the GoCardless API directly (their -1 sentinel for "last day," where supported) — it isn't exposed here.

Why does day-of-month behave differently for BACS (UK) subscriptions?2026-07-29T15:12:47+01:00

BACS is the one scheme that doesn't support the day_of_month / month subscription parameters at all. For every other scheme (SEPA Core, ACH, BECS, PAD, Autogiro, Betalingsservice…), the plugin sends day_of_month (and month, for yearly) directly in the subscription-create call. For BACS, it instead computes a concrete start_date from your day/month preference and sends that — GoCardless then just recurs the subscription forward from that date at the chosen interval. Functionally you get the same outcome, but if you're inspecting the raw API payloads while debugging, don't be surprised that a BACS subscription has a start_date and no day_of_month, while a SEPA one has the reverse.

Can I combine a free trial with a fixed payment day?2026-07-29T15:12:47+01:00

No, and this is deliberate rather than a bug. A trial delay (trial_days) determines the subscription's start date by itself; a day/month preference is a separate, incompatible way of pinning the start date. The plugin's form builder hides the day/month fields once a trial is set, and the subscription-creation code explicitly skips day/month resolution whenever a trial is active — sending both would give GoCardless two disagreeing signals about when the subscription should start.

What are the real limits on billing intervals?2026-07-29T15:12:47+01:00

Weekly: 1–104 (so "every 104 weeks" is valid, "every 105" isn't). Monthly: 1–24 — and note this supports any value in that range, not just 1, so "every 3 months" (quarterly) and "every 6 months" (semi-annual) are both entirely valid monthly subscriptions, contrary to what you might assume from a plugin that defaults to monthly billing. Yearly: always exactly 1 — GoCardless has no concept of "every 2 years" as a single subscription; you'd need to model that as a fixed-count or manually-recreated arrangement instead. These are hard GoCardless API limits, not values the plugin invented, so there's no settings flag to raise them.

If I let customers choose their own billing frequency, can I still set a preferred payment day?2026-07-29T15:12:47+01:00

No — when a form uses "customer chooses" for billing frequency (offering Weekly/Monthly/Annually as a choice on the form), the resulting subscription is always billed every 1 unit of whatever they pick, with no day-of-month preference applied. The day/month controls in the form builder only appear when you've fixed the frequency yourself. If a specific collection day matters to you, you'll need to fix the frequency rather than hand it to the customer.

Forms and data integrity

If I delete a form or an entry, does that cancel anything in GoCardless?2026-07-29T15:12:47+01:00

No — nothing at all. There are no delete hooks wired up between Gravity Forms and GoCardless in either direction. A subscription or mandate created through a WPDD form keeps running exactly as scheduled in GoCardless regardless of what happens to the originating form or entry in WordPress; you'll just lose the ability to see or manage it from inside WordPress. If you actually want to stop the payments, you need to cancel the subscription or mandate directly — either through the WPDD dashboard (while the entry/form still exists) or in the GoCardless dashboard itself.

Is the “Edit form” link from a GoCardless subscription reliable after a database restore?2026-07-29T15:12:47+01:00

Not necessarily, and it's worth understanding why before you rely on it for anything important. When enabled, WPDD writes a small ownership tag into one of GoCardless's metadata slots on each subscription it creates — a JSON blob recording the originating WordPress entry ID and form ID, which is what powers the "Edit form" shortcut in the subscriptions list. That tag is just a stored pointer; GoCardless has no idea that entry IDs are WordPress-specific or that they can be reassigned. If you ever restore a database backup taken from a different point in time, or merge/import entries from another site, WordPress can end up reusing an entry ID that a live GoCardless subscription's metadata still references — at which point the "Edit form" link (and anything else trusting that pointer) will confidently take you to the wrong entry. The webhook processing pipeline doesn't depend on this metadata at all — it always matches by mandate/payment/billing-request ID against entry meta — so functionality isn't at risk, only this one convenience link. Treat it as a UI shortcut, not a durable source of truth, especially on any site with a history of database restores or migrations.

When can a payment actually be refunded?2026-07-29T15:12:47+01:00

Two conditions have to both be true: the payment must already show status Paid out, and at least 7 calendar days must have passed since its charge date. That 7-day figure is a GoCardless-side "safer refund" hold, not something the plugin adds on top — it exists as a fraud-protection measure on their end, and there's no setting to shorten it. The Refund button simply doesn't render until both conditions hold; there's no error message to catch because the option isn't offered until then.

Webhooks

I have not set up a webhook endpoint – does this matter?2026-07-29T19:39:55+01:00

Yes, it matters — without a webhook, the very first sign-up will usually still go through, but GoCardless has no way to tell your site about anything that happens afterwards, like a payment clearing, failing, or a customer cancelling. That means statuses in your dashboard just freeze in place even though real activity is still happening on GoCardless’s side. It only takes a couple of minutes to set up, and it’s the same setup for both test and live modes.

My host has a firewall/WAF or caching layer — will that break webhooks?2026-07-29T15:12:47+01:00

Possibly, and it's worth checking. The webhook endpoint (/wp-json/wpdirectdebit/v1/webhook) is registered with permission_callback => __return_true, meaning WordPress itself imposes no authentication — the plugin verifies each request by checking the Webhook-Signature header against an HMAC of the raw payload using your webhook secret. Some WAFs and security plugins are configured to block or challenge unauthenticated POST requests to /wp-json/, which will silently stop GoCardless's webhook deliveries from ever reaching the handler. Separately, if you run a full-page cache (LiteSpeed Cache, WP Rocket, etc.), make sure the webhook endpoint — and ideally the whole /wp-json/wpdirectdebit/ namespace — is excluded from caching. A cached response to what should be a live webhook POST will look like success to GoCardless while doing nothing.

A webhook event never showed up. Where did it go?2026-07-29T15:12:47+01:00

Two likely places to check, in order: your error log, and whether the underlying entry can even be found. Each event is processed inside a try/catch specifically so a single malformed or unexpected event can't throw a 500 and cause GoCardless to retry the entire batch — a caught exception is logged and the handler moves on to the next event in the payload, returning 200 regardless. Separately, event-to-entry matching works by searching Gravity Forms entry meta for the mandate ID, payment ID, or billing request ID referenced in the event's links — there's no GoCardless-side pointer back to WordPress. If the entry that originally recorded that ID has since been deleted (GDPR erasure, manual cleanup, entry expiration), the event has nothing to attach to and is dropped without error. This is invisible from the WordPress side; if you need to debug it, check the raw payload GoCardless shows you in its own dashboard.

Why does an entry sometimes have two mandate IDs — or an unexpected scheme mismatch?2026-07-29T15:12:47+01:00

When a billing request combines a Direct Debit mandate with an Instant Bank Pay (or other instant-scheme) joining fee, GoCardless can create two mandates for the same billing request — one Direct Debit, one instant. The webhook handler explicitly filters by scheme (only bacs, sepa_core, sepa_cor1, ach, becs, becs_nz, pad, autogiro, betalingsservice count) before writing wpdd_mandate_id, specifically so the instant mandate can't overwrite the Direct Debit one that the recurring subscription actually needs. If you're building anything that reads wpdd_mandate_id directly, know that it's deliberately DD-only — it will never be the instant-payment mandate.

I want to hook into status changes for custom notifications. Should I poll `wpdd_status`?2026-07-29T15:12:47+01:00

Use the action hooks instead: wpdd_mandate_ready, wpdd_mandate_active, wpdd_payment_confirmed, wpdd_payment_paid_out, wpdd_subscription_created, wpdd_payment_failed, wpdd_subscription_create_failed, and wpdd_mandate_cancelled. All fire with ( $entry_id, $resource_id ). The reason to prefer these over reading wpdd_status meta directly is that the status field is deliberately monotonic — GoCardless doesn't guarantee event delivery order, so a new status only overwrites the stored one if it's equal or higher "priority" (a fixed ranking baked into the plugin, with terminal failures always winning regardless of order). A lower-priority event arriving late — say a billing_requests:fulfilled turning up after payments:confirmed already landed — is silently ignored rather than regressing the status. If your integration reads the raw meta value expecting it to reflect "the last webhook event," you'll occasionally be surprised; the action hooks fire on every meaningful transition and don't have this filtering.

If I migrate my site to a new domain, could a retried webhook double-charge a customer?2026-07-29T15:12:47+01:00

In theory, yes — worth knowing if you're planning a domain migration mid-flight. Idempotency keys sent to GoCardless for payment creation are built from the entry ID plus home_url() (e.g. pay-1234https://example.com), so GoCardless can recognise a retried request as a duplicate of one it already processed. If your site's URL changes between the original request and a retry — a domain migration, or switching http to https — the idempotency key changes too, and GoCardless will treat it as a brand-new request rather than a duplicate. This is a narrow window (it only matters for retries of in-flight payment creation, not for steady-state operation), but if you're cutting a domain over, it's worth doing during a quiet period rather than mid-signup-flow.