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.

8 min readUpdated 3 August 2026install, licence, extension, cron

P2P ships inside the same repository tree as the rest of the platform. There is no separate download to unzip into a directory and no schema to import by hand. Installing it is three things: prove you own it, switch the row on, and confirm the background job that owns every deadline in the product is actually running.

P2P Trading is CodeCanyon item 44593497. That number is the productId on the extensions row, the id the licence layer reports, and the id you will use in every query on this page.

Before you start

    • A working Bicrypto install — see Server requirements
    • Your CodeCanyon purchase code for P2P Trading (item 44593497)
    • 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. Activate the licence — in the admin panel, open the extensions screen at /admin/system/extension, find P2P, and enter your Envato purchase code.

    The activation call is POST /api/admin/system/license/activate with the purchase code; the Envato username is auto-detected from the code, and a notification email is optional. There is also an offline path (activate-from-file) for boxes with no outbound internet, which takes a .lic file bound to that machine.

    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.

  2. Switch the extension on — the same screen has the status toggle. It writes status = true on the extensions row whose productId is 44593497, then clears the settings cache so the other processes see it.

    If the toggle flips on screen but the section never appears, reload the page and check the toggle 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.

  3. Restart the backend and cron processes — from the project root:

    pnpm restart

    New routes are loaded at boot. The cron registry is also built at boot, so the P2P jobs do not appear until the cron process has been restarted.

  4. Confirm the tables exist — the schema auto-syncs on boot. 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 deadline in P2P is enforced by that one-minute job. If the cron process is not running, an unpaid trade stays PENDING forever, the seller's funds stay in inOrder, and the offer never gets 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. Too short and honest buyers lose trades to a bank's processing time.
    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 let it expire instead, and confirm the escrow comes back.

Step 5 is the one people skip. Every deadline in P2P — payment windows, escrow return, 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 forever and the seller's funds stay locked. 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.

# 1. The extension row is on
mysql -e "SELECT productId, name, status, version FROM extensions WHERE productId='44593497'" YOUR_DB

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

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

# 4. 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 step 4 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

Switch the extension row off. That removes the menu and the routes; it does not delete data, and it does not release escrow.

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.