GatewayChanger for nopCommerce
One plugin, both roles. A nopCommerce 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 is a C# port of the same protocol the WooCommerce plugin implements, class for class and field for field, so a nopCommerce 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 nopCommerce, 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. It also needs a build chain we are still standing up — the .NET 9 SDK and a nopCommerce 4.90 source tree — so of the four new platforms this is the one most likely to move. This page is the build target, 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
| nopCommerce | 4.90 (SupportedVersions: ["4.90"], developed against 4.90.8). Earlier lines are a separate build. |
| .NET | 9.0 runtime — whatever your nopCommerce 4.90 installation already runs on |
| System name | Payments.GatewayChanger; the folder under ~/Plugins/Uploaded must keep that name |
| Checkout | The standard payment step, including one-page checkout. Embedded and redirect modes both. |
| Database | Its own tables, created by the plugin's migration on install. No core table is altered. |
| Scheduled tasks | Three tasks registered on install (heartbeat, outbox, config sync). The nopCommerce task scheduler must be running. |
| Transport | HTTPS on both stores, and the plugin's routes reachable from the internet without a redirect. |
Install
- Download
Payments.GatewayChanger.zipfrom Plugins in your GatewayChanger dashboard. The card shows the version and its SHA-256 checksum. (No package yet — see the status note above.) - Configuration → Local plugins → Upload plugin or theme, pick the zip. The archive has exactly one root folder
with
plugin.jsoninside it, which is what nopCommerce expects. - Restart the application when nopCommerce asks. The plugin list is read at startup.
- Find GatewayChanger in the list and press Install, then restart once more.
- Configuration → Payment methods, tick Is active on GatewayChanger, then press Configure.
Step 5 is not optional and it is the usual reason a payment method never appears: installing a plugin does not add it to the store's active payment methods. Four independent switches have to be on — installed, active, not hidden by the store/customer-role limits, and connected.
Because the views ship as .cshtml and are compiled at runtime, an upgrade is upload again and restart; the plugin
cannot hot-swap its own assembly, so the dashboard will only ever tell you an update exists, never apply it for you.
Connect the shop
Register the shop in the dashboard first (Shops → Add shop, platform nopCommerce), then on the plugin's configure page, under Connection:
- Leave the API base at
https://gatewaychanger.com/api/v1unless you were told otherwise. - Paste the shop key from the dashboard —
shk_test_…in the test environment,shk_live_…in live. - Press Connect, then Save.
The plugin generates an Ed25519 key pair (the private key never leaves your server), registers the public key, and reports its roles and capabilities. You then see the shop name, a TEST MODE or LIVE badge, the verification status and the time of the last heartbeat.
The charger API lives under https://your-store.example/plugins/gatewaychanger/v1/… — the configure page prints the
exact base — and we call {api_base}/capabilities 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 reported base URL has to
be the final one: no apex↔www hop, no http→https hop, and mind nopCommerce's own Use SSL and www requirement
settings, which redirect anything that does not match.
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 configure page is only about the connection. Roles, checkout texts, charger settings and payment processors live
in the GatewayChanger dashboard (Shops → your shop), and the plugin 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. A scheduled task re-reads the configuration as a fallback if a ping is ever missed. The configure page shows
the applied settings 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 plugin applied, the last sync ping and its result, a Sync now button, and any errors the plugin 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 method 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 topic 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 Pending until the charge is captured. |
The payment-info view component renders the consent checkbox and the charger's iframe inside nopCommerce's own form —
no nested form, and the postMessage handler is inline so it survives one-page checkout's AJAX step injection. The
plugin creates the session, re-routes on finalize if the total changed, and passes the session id, the payment token
and the consent hash through CustomValues, marked not displayed to the customer. Totals are taken in the primary
store currency and rounded once, at the edge.
The order page records the routing in an order note:
Charged €24.99 via Shop B Ltd (Stripe) — ref pi_3Q1… — routed by rule "EU cards over €50" (decision dec_01J8…)
Capture, void and refund use the standard IPaymentMethod members, so partial refunds from the order page behave the
way the rest of your store does; they are forwarded to the charger and executed at the processor.
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. Only your store can open
it; the API rejects any credential that is not sealed, and the value is never shown again. Reinstalling the plugin
creates a new key, so anything sealed to the old one is flagged re-enter required.
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
- In the Stripe dashboard, switch to test mode and open Developers → API keys.
- In the GatewayChanger dashboard, Charger & processors → Add a processor → Stripe, and paste the publishable
key (
pk_test_…) and the secret key (sk_test_…). - Add a webhook endpoint pointing at the URL the configure page prints for you, subscribe to
payment_intent.*,charge.refundedandcharge.dispute.*, and paste the signing secret (whsec_…) into the same dialog. - Choose the capture method (automatic or manual) and, optionally, a statement descriptor suffix.
- 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 nopCommerce 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. nopCommerce's normal order placement reads the customer's shopping cart and refuses an empty one, so the mirror order is written directly with its own line, addresses and totals, and carries the seller's order number and the decision id.
Testing the whole flow
- Connect two stores, one seller and one charger, both in the test environment.
- Enable the test processor at the charger.
- Create one catch-all rule pointing at the charger, with "seller charges itself" as a second candidate.
- Check out with
4111 1111 1111 1111→ captured. - Retry with
4000 0000 0000 0002→ declined; the iframe resets and asks for another card, and no failover happens. - Set the cart total to exactly 44.00 →
processor_error; the seller re-routes with the charger excluded and the second candidate takes it. - Refund half the order from the order page and watch the refund appear on the mirror order and in the ledger.
Do step 4 on one-page checkout as well as the standard one. The payment step is injected by AJAX there, which is where iframe integrations usually break.
Troubleshooting
The payment method never appears. In order: the plugin is installed; Is active is ticked on Configuration →
Payment methods; the store or customer-role limits in plugin.json are not excluding it; the shop is connected and
a candidate supports the cart currency. Restart the application after installing.
The plugin vanished from Local plugins after an upload. A stale assembly in ~/Plugins/. Stop the site, delete
the old Payments.GatewayChanger folder, upload again, restart.
A callback gets an HTML page instead of JSON. nopCommerce re-executes errors and 404s into themed pages. Our API controller returns its own JSON envelope with an explicit status code; if you are seeing HTML, the request is not reaching the controller — check the route, the SSL/www redirects and any reverse proxy.
"Session expired" at checkout. Grants live 30 minutes. A checkout left open longer needs a new session; reloading the payment step 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. The scheduled task is disabled, or the task scheduler is not running — check Configuration → Schedule tasks, and restart the application after installing, since the task list is read at startup. Fifteen minutes without a heartbeat removes the shop from routing.
Where this stands
- Written and reviewed against the nopCommerce 4.90 plugin contract (
IPaymentMethod, view components, areas, schedule tasks, the upload format) 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 (nopCommerce as seller against a WooCommerce charger, then
the reverse), and a field-by-field diff of
GET /capabilitiesagainst the WooCommerce document. - The C# core is reviewed class by class against the PHP core the other plugins share, because "the same protocol" has to mean the same bytes on the wire, not the same intentions.
- Out of scope for 1.0.0: self-update (upload + restart is the only safe upgrade on .NET) and the dashboard's SSH auto-installer.
- If you run nopCommerce 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.