Skip to content
GatewayChanger
In build Zen Cart 2.1+ · PHP 8.1+

GatewayChanger for Zen Cart

One plugin, both roles. A Zen Cart store can sell (route its checkout to a partner shop that charges the card) and charge (accept routed payments with its own processor accounts) — often both at the same time. It speaks the same protocol as the WooCommerce plugin, with the same field names and error codes, so a Zen Cart store can sell to a WooCommerce charger and the other way round.

Status: in build — not yet verified against a live store (2026-09-22). We are writing this plugin now and targeting version 1.0.0. It has not run on a production Zen Cart, there is no zip on the Plugins page yet, and it has not been through the end-to-end conformance suite that the WooCommerce and Magento plugins pass. This page is the build target — published early so you can see exactly what you would be getting and tell us now if it does not fit your store. It is not a release note. We would rather show you a date than a checkmark we cannot back: when the first build passes the gate, the date and the version appear here and in the changelog.

Requirements

Zen Cart 2.1.0 or newer (developed against 2.1.0 and 2.2.x). The installer refuses anything older.
Why 2.1.0 Payment modules are only discovered inside zc_plugins from 2.1.0. On 1.5.8 or 2.0.x the module would simply not appear at checkout.
PHP 8.1 or newer with ext-sodium — the installer checks for sodium_crypto_sign_verify_detached and stops if it is missing
Checkout The standard three-step checkout. Embedded and redirect modes both.
Database Its own gwc_* tables and a handful of configuration keys. No core table is altered.
Cron A host cron running the plugin's admin-side CLI script every five minutes — it sends the heartbeat.
Transport HTTPS on both stores, and the plugin's two root entry points reachable from the internet without a redirect.

Install

Zen Cart installs in two stages, and skipping the second is the most common "it does not work" report on any Zen Cart payment module.

  1. Download gatewaychanger-zencart.zip from Plugins in your GatewayChanger dashboard. The card shows the version and its SHA-256 checksum. (No package yet — see the status note above.)
  2. Unzip it and copy the zc_plugins/ folder into your shop root, so you end up with zc_plugins/GatewayChanger/v1.0.0/…. Nothing else moves; the admin and catalog folders inside it are literal and are not renamed to your obfuscated admin directory.
  3. Admin → Tools → Plugin Manager. GatewayChanger appears with its version and description; press Install. This runs the scripted installer: the version and libsodium checks, the tables, the configuration keys, and the two small entry-point files it copies into the shop root.
  4. Admin → Modules → Payment → GatewayChanger → Install. This is the step people miss. The Plugin Manager only makes the files visible; the payment module itself is installed here, and this is what fills in its configuration.
  5. Open its settings and set the status to True.

Upgrading means dropping a new v1.0.1/ directory beside v1.0.0/ and pressing Upgrade in the Plugin Manager; every version directory is a complete fileset, never a diff. Uninstalling removes the configuration keys, the tables and the root entry points.

Connect the shop

Register the shop in the dashboard first (Shops → Add shop, platform Zen Cart), then in the module's settings under Modules → Payment → GatewayChanger:

  1. Leave the API base at https://gatewaychanger.com/api/v1 unless you were told otherwise.
  2. Paste the shop key from the dashboard — shk_test_… in the test environment, shk_live_… in live.
  3. Save. The module connects on the next admin page load and shows the result.

The module generates an Ed25519 key pair (the private key never leaves your server), registers the public key, and reports its roles and capabilities. The settings page then shows the shop name, a TEST MODE or LIVE badge, the verification status and the time of the last heartbeat.

Zen Cart has no router, so the charger's machine-facing endpoints are two small PHP files that the installer copies into your shop root — gwc_api.php and gwc_webhook.php. Your API base is therefore https://your-store.example/gwc_api.php, and we call it to verify the shop. Until that succeeds the shop is pending_verification and cannot be a charger. The probe does not follow redirects, so the base URL has to be the final one: no apex↔www hop, no http→https hop.

Key rotation generates a new pair and keeps the previous public key valid for seven days, so in-flight sessions do not break.

Everything else is configured in the dashboard

The module's settings hold the connection and nothing else. Roles, checkout texts, charger settings and payment processors live in the GatewayChanger dashboard (Shops → your shop), and the module pulls them: we ping it whenever you save, it fetches GET /api/v1/shops/self/config, applies the document atomically, tests every enabled processor and reports back. The applied settings are shown read-only with a Managed in the GatewayChanger dashboard note.

The shop detail page shows the state of that loop under Connection & plugin: the version you saved, the version the module applied, the last sync ping and its result, a Sync now button, and any errors the module reported.

Seller role

Turn on the seller role and the seller settings in the dashboard (Seller settings tab).

Setting Notes
Title / description What the customer sees in the payment list — for example "Credit / debit card".
Consent label Defaults to the template we send; keep who charges, on whose behalf, and the amount.
Terms page The EZ-page or information page linked from the consent line. It needs the payment-processing clause.
Payment mode embed (recommended — the customer stays on your checkout) or redirect.
When no route is available Hide the payment method, or charge on this shop itself.
Order status for authorizations Usually a pending status until the charge is captured.

The module follows Zen Cart's own checkout sequence: it offers itself on the payment page, routes and fetches the grant before confirmation, renders the consent checkbox and the charger's iframe on the confirmation page, charges before the order is written, and then adds the routing to the order's status history:

Charged €24.99 via Shop B Ltd (Stripe) — ref pi_3Q1… — routed by rule "EU cards over €50" (decision dec_01J8…)

Amounts are read from the order in the customer's currency, using that order's own currency value and decimal places, and converted to integer minor units once — Zen Cart's decimal places are a display setting and cannot be trusted for arithmetic.

Zen Cart has no refund table, so the plugin adds its own refund action in the admin order view; it is forwarded to the charger, executed at the processor, and reported back to both sides.

Charger role

In the dashboard, open the Charger & processors tab: turn on the charger role, set the legal name exactly as it should appear in the consent sentence and on receipts, then add at least one processor.

Credentials are end-to-end encrypted. On activation the plugin generates an X25519 key pair alongside its signing key and reports the public half. When you type a secret in the dashboard, your browser seals it to that key (crypto_box_seal) and only the ciphertext — sb1.<fingerprint>.<…> — is stored and synced. The plugin opens it on your server and keeps it in its own table under a second layer of encryption, not in the configuration table where an admin page would print it. The API rejects any credential that is not sealed, and the value is never shown again.

Test processor

No credentials. Deterministic outcomes by card number — see test cards. Use it for the first end-to-end run and for continuous integration; it is free and never talks to a third party.

Stripe

  1. In the Stripe dashboard, switch to test mode and open Developers → API keys.
  2. In the GatewayChanger dashboard, Charger & processors → Add a processor → Stripe, and paste the publishable key (pk_test_…) and the secret key (sk_test_…).
  3. Add a webhook endpoint pointing at https://your-store.example/gwc_webhook.php?processor=stripe — the module's settings page prints the exact URL — subscribe to payment_intent.*, charge.refunded and charge.dispute.*, and paste the signing secret (whsec_…) into the same dialog.
  4. Choose the capture method (automatic or manual) and, optionally, a statement descriptor suffix.
  5. Repeat with live keys when you go live — and only then.

Both keys are encrypted in your browser before they are sent and can only be decrypted inside your Zen Cart installation. GatewayChanger never receives them in readable form, never proxies a call to Stripe, and has no way to charge on your behalf. After you save, the plugin tests the keys and the dashboard shows connected or the exact error — and routing skips a processor that is not connected.

Mirror orders

Every charge creates an order in your own store with a single line — "Payment on behalf of {seller} order {number}" — so your bookkeeping, tax reporting and refund workflow keep working the way they already do. The seller's order number and the decision id are stored on it and shown in its status history.

Testing the whole flow

  1. Connect two stores, one seller and one charger, both in the test environment.
  2. Enable the test processor at the charger.
  3. Create one catch-all rule pointing at the charger, with "seller charges itself" as a second candidate.
  4. Check out with 4111 1111 1111 1111 → captured.
  5. Retry with 4000 0000 0000 0002 → declined; the iframe resets and asks for another card, and no failover happens.
  6. Set the cart total to exactly 44.00 → processor_error; the seller re-routes with the charger excluded and the second candidate takes it.
  7. Refund half the order from the admin order view and watch the refund appear on the mirror order and in the ledger.

Watch the decline in step 5 closely. Zen Cart counts failed payment attempts and destroys the customer's session on the fourth, so the module treats a card decline as a retry inside the iframe rather than as a failed checkout.

Troubleshooting

GatewayChanger is not in the payment list at all. Either the Plugin Manager step ran but Modules → Payment → Install did not (see install step 4), or the module's language file is missing or misnamed — Zen Cart then skips the module silently, leaving only a caution in the message stack. Also check that the status is True and that the sort order is set.

Zen Cart is below 2.1.0. The plugin refuses to install, on purpose. Older Zen Cart does not discover payment modules inside zc_plugins, and we would rather stop than half-work.

The root entry points 404. gwc_api.php and gwc_webhook.php are copied into the shop root by the installer; a deploy that syncs only tracked files, or an uninstall/reinstall, can leave them out. Re-run the Plugin Manager install, or copy them from the plugin's support folder.

"Session expired" at checkout. Grants live 30 minutes. A checkout left open longer needs a new session; reloading the confirmation page is enough.

The iframe is blank. The embed page restricts frame-ancestors to the seller origin. Mismatched www/apex or http/https between the configured base URL and the page the customer is on will block it.

Heartbeats stop. Zen Cart has no scheduler; the plugin ships a CLI script under your admin directory and the settings page prints the crontab line for it. Fifteen minutes without a heartbeat removes the shop from routing.

Where this stands

  • Written and reviewed against the Zen Cart 2.x encapsulated-plugin contract (Plugin Manager, scripted installer, payment-module lifecycle, language files) and the same protocol document the other plugins implement.
  • Not yet installed on a live store, not yet through the four-part conformance gate: the independent signing client, the unit tests, a cross-platform end-to-end run (Zen Cart as seller against a WooCommerce charger, then the reverse), and a field-by-field diff of GET /capabilities against the WooCommerce document.
  • Out of scope for 1.0.0: the loose-file build for 1.5.8 / 2.0.x, self-update from the dashboard, and the dashboard's SSH auto-installer. The first release is download-and-copy.
  • If you run Zen Cart and want to be in the first round of real installs, mail [email protected] — we would rather find the surprises with you than after you.

Plugins marked “In build” are published here only once a real test store has routed a real order end to end.


Something wrong or missing on this page? Tell us and we will fix it.