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.
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
ECOwallet offers
Steps
-
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/activatewith 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.licfile bound to that machine.Every route under
/api/p2pand/api/admin/p2pis registered in the extension licence map. Until the licence validates, those routes are gated — the pages render and every request behind them fails. -
Switch the extension on — the same screen has the status toggle. It writes
status = trueon theextensionsrow whoseproductIdis44593497, 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.
-
Restart the backend and cron processes — from the project root:
pnpm restartNew 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.
-
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_logsandp2p_admin_activity. -
Confirm the cron jobs are registered — open the scheduled task screen in the admin panel and look for the
p2pcategory. 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
PENDINGforever, the seller's funds stay ininOrder, and the offer never gets its capacity back — and nothing in the UI tells you it is happening. -
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-methodand 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. -
Set the four settings that matter on day one — at
/admin/p2p/settings:Setting Default Set it because p2pAutoApproveOffersoff 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. p2pDefaultPaymentWindow15 min Applies to every offer that does not set its own. Too short and honest buyers lose trades to a bank's processing time. p2pEscrowFeeRate0.2 % The only fee P2P charges. Taken out of the crypto delivered to the buyer. p2pMinimumTradeAmount/p2pMaximumTradeAmount10 / 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.
-
Grant the permissions — the P2P admin screens are gated on their own permission keys. A role that can reach
/adminstill 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_methodGive your dispute handlers
view.p2p.disputeandedit.p2p.dispute. Note thatedit.p2p.tradeis the key that lets someone force-resolve a trade and move escrow, so hand it out sparingly. -
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 400If 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.