Install and enable

Licence the Payment Gateway extension, switch it on, set the environment variable that makes checkout URLs reachable, configure the wallet types that gate every payment, and verify the three cron jobs.

11 min readUpdated 26 August 2026install, license, cron, settings

The gateway ships as an extension of an already-working Bicrypto install. There is no separate installer and no database migration you run by hand — the models are auto-synced with the rest of the schema on boot. What you do here is licence it, install it, switch it on, and set the four things that are wrong by default.

Do the core install first. If the platform is not already serving traffic over HTTPS with a working cron process, nothing on this page will help.

Before you start

  • A running Bicrypto install (frontend, backend and cron all up)
  • Your purchase code for Payment Gateway (product ID 61043226), from your MashDiv dashboard
  • Outbound HTTPS to updates.mashdiv.com from the app server
  • Shell access to run pnpm updator and restart PM2
  • The Ecosystem addon, only if you intend to accept ECO payments

Steps

  1. The Payment Gateway card — click it to open the product page
  2. Its switch stays disabled until the licence is activated
  1. Activate the licence — go to Admin → System → Extension Manager (the page heading is Add-ons & Integrations). On the Extensions tab, or through the search box, find the Payment Gateway card — product ID 61043226 — and click it. Until its licence is activated the card reads Activate and its switch is disabled with the tooltip Activate license first.

    On the product page press Activate License. Paste the purchase code from your MashDiv dashboard, optionally a Notification Email for update notices, and press Activate License. The screen confirms License Activated! and sends you back to the product page a couple of seconds later. Activation works even on a platform whose own licence is mid-renewal. If the server has no outbound HTTPS, the License File tab on the same screen takes the licence certificate downloaded from your MashDiv dashboard instead — place it as license.txt in the /lic folder at the project root first.

    1. Paste the purchase code here
    2. Activate License — then wait for the redirect back to the product page
    3. The offline route, for a server with no outbound HTTPS
  2. Install the release — back on the product page, now licensed, the Overview tab has a release panel. If it offers Install v…, press it; if it says Up to date on a product you have only just licensed, press Check for Updates once, then install whatever it offers. The Releases tab holds the notes for the version you are being offered — read them first.

    Install downloads the release, verifies it and extracts it over the project root. It runs no migrations, builds nothing and restarts nothing, so finish it from a shell at the project root:

    pnpm updator

    That is the finalise chain — stop, dependencies, schema, seed data, frontend build, start. If the panel still says Up to date after the check there is nothing to download; go straight to the next step.

    1. Check for Updates, then Install v… when it is offered
    2. Releases — the notes for the version you are about to install
    3. The Enabled switch — step 3, not yet
  3. Switch it on — turn the Enabled switch on, in the product page heading or on the card in the Extension Manager, which is usable now that the licence is verified. Nothing under /api/gateway or /api/admin/gateway responds until it is on, and the Gateway menu item does not appear for users. Turning it off later is the same switch; it leaves the licence in place, so switching back on needs no reactivation.

  4. Restart the backend and cron — routes are registered at boot.

    pnpm restart
  5. Set APP_PUBLIC_URL — see below. This is the step most installs miss.

  6. Configure allowed wallet types — see below. Until you do, every payment is rejected.

  7. Grant the permissions — the admin screens and endpoints are gated by twelve *.gateway.* keys. Assign them to whichever role staffs the queues.

APP_PUBLIC_URL is not optional here

The checkout URL returned by the payment API is built as ${APP_PUBLIC_URL}/${APP_DEFAULT_LOCALE}/gateway/checkout/pi_…. APP_PUBLIC_URL ships empty in .env.example, and the fallback is http://localhost:3000.

The API returns 201 Created, the merchant's integration redirects the buyer, and the buyer's browser tries to open a page on their own machine. Nothing errors on your side. The merchant reports that "checkout is broken" and there is no log line to find.

APP_PUBLIC_URL="https://exchange.example.com"

APP_DEFAULT_LOCALE is not in .env.example at all. It defaults to en. If your install runs on a different default locale, set it explicitly or every checkout link will carry an /en/ prefix your router has to redirect.

Restart the backend after editing .env — it is read once at boot.

Allowed wallet types gate everything

  1. Open the Wallets tab

gatewayAllowedWalletTypes is a JSON map of wallet type to { enabled, currencies }. Its default is an empty object, and an empty map means no wallet type is enabled, which means:

  • POST /api/gateway/v1/payment/create fails with "Wallet type FIAT is not enabled for payments".
  • The checkout page offers the buyer no wallets to pay from.
  • New merchants are created with the fallback ["FIAT"] / ["USD"], which the platform check then rejects anyway.

Configure it under Admin → Extensions → Payment Gateway → Settings → Wallets before you tell anyone the gateway is live. Enable each wallet type you want and list the exact currency codes under it. The check is a literal includes() against an upper-cased code, so usd and USD are not the same thing and a currency that is enabled for FIAT is not enabled for SPOT.

A payment is checked twice: against the merchant's own allowedCurrencies / allowedWalletTypes columns, and against this platform-wide map. A currency present in one and absent from the other is rejected, with a different error message each way — "not supported by this merchant" versus "not enabled for payments". That wording is the fastest way to tell which list is short.

The three cron jobs

The gateway registers three jobs in the gateway category. They run in the cron worker, not the API process.

Job Every What breaks without it
processGatewayPayouts 60 min No payout record is ever created. pending balances grow forever and no merchant can be paid.
processGatewayWebhookRetries 60 s A merchant endpoint that is down for one minute loses the event permanently.
processGatewayPaymentExpiry 5 min Abandoned sessions sit at PENDING forever and payment.expired never fires.

Check them under Admin → System → Cron. If the whole category is missing, the gateway's Enabled switch is off or the cron worker is not running.

processGatewayPayouts reads gatewayEnabled and skips the entire run when it is off — while payments keep completing and crediting pending. Turning the gateway "off" does not stop money arriving; it stops money leaving.

Permissions to assign

Twelve keys are seeded. Nothing grants them automatically except Super Admin.

Key Gates
access.gateway.merchant The admin dashboard and merchant screen
view.gateway.merchant Merchant list and detail
edit.gateway.merchant Status, verification and merchant edits
delete.gateway.merchant Deleting a merchant
access.gateway.payment · view.gateway.payment Payment screens
manage.gateway.payment Issuing an admin refund
view.gateway.payout Payout list and detail
edit.gateway.payout Approving and rejecting payouts
view.gateway.refund The refund list endpoint
access.gateway.settings · view.gateway.settings The settings screen

Two notes that matter when you build a role:

  • The payout screen has no page-level permission file. The /admin/gateway/payout page itself is not gated; its data endpoints are (view.gateway.payout), so a staff member without them sees an empty screen rather than a 403.
  • Saving settings needs edit.settings, not edit.gateway.settings. The settings screen writes through the core settings endpoint. edit.gateway.settings guards a second, parallel endpoint the UI does not use. Give the role both if you want the screen to work.

See Permissions for how a key is derived and the places it has to exist.

Verify the install

The routes are live

From the app server:

curl -s -o /dev/null -w "%{http_code}\n" https://exchange.example.com/api/gateway/settings

200 means the extension is enabled and licensed. 503 means the route registered but the licence check failed. A 404 means the extension is switched off or the backend has not been restarted since you enabled it.

The public settings endpoint returns your wallet map
curl -s https://exchange.example.com/api/gateway/settings

The response should include gatewayAllowedWalletTypes with at least one enabled type and a non-empty currency list. {} or a missing key means step 6 was not done.

The menu appears

Signed in as a normal user, Gateway should appear in the main navigation and /gateway should render the landing page. In the admin panel, Payment Gateway appears under Admin → Extensions and needs access.gateway.merchant.

The cron category exists

Admin → System → Cron should list Process Gateway Payouts, Retry Failed Gateway Webhooks and Expire Lapsed Gateway Payments, all idle rather than failed.

A test payment completes end to end

Register a merchant, approve and verify it, then use its sk_test_ key to create a payment and open the returned checkoutUrl in a browser as a second user with a funded wallet. A test-mode payment debits nothing and credits nothing, but it exercises the wallet selection, the exchange-rate check and the webhook path.

What is not installed for you

  • No demo merchant, no seed data. The gateway_* tables start empty.
  • No default currency configuration. The wallet map is {}.
  • No reverse proxy change. The gateway lives under /api, which your proxy already forwards.
  • No WooCommerce plugin on disk anywhere public. Merchants download it from /gateway/integration, which zips it out of the backend source tree at request time. If your deployment strips non-TypeScript files from backend/src, that download 500s with an explicit message telling you the plugins directory is missing.

Next: Merchant onboarding.