Install and enable P2P

Activate the P2P licence, switch the extension on, verify the tables and the cron job, and set the handful of options that decide whether the marketplace is usable on day one.

14 min readUpdated 26 August 2026install, licence, extension, cron

There is no archive to unzip by hand and no schema to import. Installing P2P is four things: prove you own it, install the release, switch it on, and confirm the background job that owns every deadline in the product is actually running.

In the Extension Manager, P2P is the Peer To Peer Exchange card — product ID 44593497, the id the licence layer reports and the id the licence screen shows during activation.

Before you start

  • A working Bicrypto install — see Server requirements
  • The purchase code for Peer To Peer Exchange, from your MashDiv dashboard
  • Outbound HTTPS from the server to updates.mashdiv.com
  • Redis reachable — P2P refuses money-moving requests without it
  • The cron process running (CRON_MODE=only, port 4001)
  • The Ecosystem addon, only if you intend to allow ECO wallet offers

Steps

  1. The Peer To Peer Exchange card — click it to open the product page
  2. Its switch stays disabled until the licence is activated
  1. Open the Extension Manager and activate the licence — go to Admin → System → Extension Manager (/admin/system/extension; the page heading is Add-ons & Integrations). On the Extensions tab, or through the search box, find the Peer To Peer Exchange card and click it. Until the licence is activated the card's chip 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 add a Notification Email for update notices, and press Activate License. The screen confirms License Activated! and returns you to the product page a couple of seconds later. If the server has no outbound internet, the License File tab on the same screen is the offline path: download the licence certificate from your MashDiv dashboard, place it as license.txt in the /lic folder at the project root, and press the button on that tab.

    Every route under /api/p2p and /api/admin/p2p is registered in the extension licence map. Until the licence validates, those routes are gated — the pages render and every request behind them fails.

    1. Paste the purchase code here
    2. Activate License — then wait for the redirect back to the product page
    3. License File — the offline route for a server with no outbound HTTPS
  2. Install the release — back on the product page, now the licensed view, 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 on offer — 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 on the server:

    pnpm updator

    That is the finalise chain — stop, dependencies, schema, seed data, frontend build, start — and it is what makes P2P's tables, routes and screens exist. If the panel still says Up to date after the check there is nothing to download — the release already on the server is current — and you move straight on to switching it on.

    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. This is what makes the P2P section appear in the admin panel and the P2P marketplace appear to users, and it takes effect without a restart. One restart is still owed, though: the cron registry is built at boot, so the two P2P jobs do not exist until the cron process has been restarted after the switch goes on. From the project root:

    pnpm restart

    If the switch flips on screen but the section never appears, reload the page and check it again. The endpoint used to answer 200 with an error in the body, which reads as success; it now throws properly, but a stale browser tab can still show the old state.

  4. Confirm the tables existpnpm updator applied the schema in step 2. Ten tables belong to P2P:

    SHOW TABLES LIKE 'p2p\_%';

    You should see p2p_offers, p2p_trades, p2p_disputes, p2p_payment_methods, p2p_offer_payment_method, p2p_offer_flags, p2p_reviews, p2p_commissions, p2p_activity_logs and p2p_admin_activity.

  5. Confirm the cron jobs are registered — open the scheduled task screen in the admin panel and look for the p2p category. Two jobs must be there:

    Job Runs What it owns
    P2P Trade Timeout Handler every 1 minute Expires unpaid trades, returns their escrow, auto-disputes trades stuck 24h after payment was declared, and expires dead offers
    Update P2P Reputation Scores hourly Recomputes completion rate and review averages, records trade-count milestones

    Every unattended deadline in P2P is enforced by that one-minute job. If the cron process is not running, nothing expires by itself. A pending trade can still be ended by hand — the buyer at any time, the seller once the payment window has passed, an admin from the case desk at /admin/p2p/trade — and each of those returns the escrow to the offer's capacity. But until somebody acts, the seller's funds stay in inOrder and the offer does not get its capacity back, and nothing in the UI tells you it is happening.

  6. Create at least one payment method — a P2P offer cannot be published without one, and users start with an empty list.

    Go to /admin/p2p/payment-method and add the methods your market actually uses. A method created here is global: available to every user without them having to define it. Users can also add up to 20 private methods of their own. See Payment methods.

  7. Set the four settings that matter on day one — at /admin/p2p/settings:

    Setting Default Set it because
    p2pAutoApproveOffers off Off means every offer waits in a queue for a human. That is the right default and the wrong one to forget about — nothing reaches the board until somebody approves it.
    p2pDefaultPaymentWindow 15 min Applies to every offer that does not set its own. It no longer cuts a late buyer off — they can still confirm — but past it the seller may cancel on demand, so too short means honest buyers lose trades to a seller who stopped waiting.
    p2pEscrowFeeRate 0.2 % The only fee P2P charges. Taken out of the crypto delivered to the buyer.
    p2pMinimumTradeAmount / p2pMaximumTradeAmount 10 / 100000 US dollars, converted per offer at the current fiat rate. Enforced server-side at offer creation and at trade initiation.

    Full field-by-field detail is on Admin settings.

  8. Grant the permissions — the P2P admin screens are gated on their own permission keys. A role that can reach /admin still cannot see any of them until it holds these:

    access.p2p
    view.p2p.trade      edit.p2p.trade
    view.p2p.offer      edit.p2p.offer
    view.p2p.dispute    edit.p2p.dispute
    view.p2p.activity
    view.p2p.payment_method    create.p2p.payment_method
    edit.p2p.payment_method    delete.p2p.payment_method

    Give your dispute handlers view.p2p.dispute and edit.p2p.dispute. Note that edit.p2p.trade is the key that lets someone force-resolve a trade and move escrow, so hand it out sparingly.

  9. Do a live end-to-end trade before you announce it — with two test accounts, on a small amount, in a currency you can afford to lose track of. Publish a SELL offer, take it from the other account, confirm payment, release, and check both wallets. Then repeat and cancel it instead — as the seller, which is allowed once the payment window has passed — and confirm the escrow goes back to the offer. Do not sit waiting for it to expire: the sweep only ends an unpaid trade a further 24 hours after the window.

Step 5 is the one people skip. Every unattended deadline in P2P — the sweep that ends an unpaid trade 24 hours after its payment window, the 24-hour stale-payment safety net, offer expiry — is enforced by a single job that runs every minute in the cron process.

With that process down, an unpaid trade stays PENDING until a person acts: the seller may cancel it once the payment window has passed, and an admin may cancel it from the case desk. Until one of them does, the seller's funds stay locked, and nothing in the customer UI or the admin panel says so.

Verify the install

Four checks, in the order that a failure is most likely. The first is the Extension Manager itself: the Peer To Peer Exchange card reads Verified and its switch is on. The other three run from a shell:

# 1. The tables exist
mysql -e "SHOW TABLES LIKE 'p2p\\_%'" YOUR_DB

# 2. Redis is answering — P2P returns 503 on trade actions without it
redis-cli ping

# 3. The public market endpoint answers (it is deliberately unauthenticated)
curl -s "https://YOUR_HOST/api/p2p/market/board?side=buy&limit=1" | head -c 400

If the last check returns a licence or forbidden error, the licence has not validated — that gate sits in front of the whole /api/p2p prefix, including the public routes.

KYC gates

P2P participates in the platform's per-feature KYC enforcement. Two feature keys apply:

Feature key Gates
make_p2p_offer Creating an offer, and editing one — an edit republishes price and limits, so it is the same commitment
buy_p2p_offer Opening a trade against any offer, either side

If you have not switched platform KYC on at all, both gates fail open — there is no verification for anyone to hold, so enforcing them would refuse every user forever with no action they could take to qualify. Once KYC is live, these are the two switches that decide who may trade.

Separately, a maker can tick KYC required on an individual offer. That checks the taker against the same primitive, so it means the same thing as the platform gate rather than something subtly different.

Rate limits you inherit

These are fixed in code, not settings. They matter because the first thing a new marketplace does is trip them during testing.

Action Limit
Create an offer 5 per hour
Initiate a trade 20 per hour
Trade actions (release, review) 50 per hour
Chat messages 100 per hour
Create a payment method 15 per hour
Open a dispute 3 per 24 hours
Search / trade history 120 per minute
Admin dispute and trade actions 100 per hour each
Admin offer actions 50 per hour

Uninstalling

Turn the Enabled switch off — on the product page or on its card in the Extension Manager. That removes the menu and the routes; it does not delete data, and it does not release escrow. The licence stays in place, so switching back on later needs no reactivation.

Every ACTIVE SELL offer is holding its advertised total in the maker's inOrder, and every open trade is holding its amount. With the routes gated, there is no door left to release either — and the timeout cron will not help, because the escrow it returns belongs to trades, not to offers that nobody can now delete.

Drain first: pause the book (p2pAllowNewOffers off), let open trades finish or expire, then delete or disable the remaining offers so their escrow is released, and only then switch the extension off.