GET STARTED

Documentation

Install, accept payments and look after your server.

VPS requirements

Use a fresh Linux VPS with root access, not an existing website/database server. No Docker, compiler or blockchain nodes needed.

VPSMinimum
Light use
Recommended
CPU1 vCPU2 vCPU
RAM2 GB4 GB
SSD20 GB60 GB

The minimum is a light-use starting point for minimal Ubuntu/Debian images, not a performance guarantee. Disk sizes include Linux; leave at least 3 GiB free before setup, plus room for updates, history and backups.

The native installer requires x86-64, systemd 247+ and Python 3.9+. ARM64 and Alpine/OpenRC are not included.

Linux versions and sizing notes
  • Ubuntu 22.04+ or Debian 12+; Mint 21+ and Pop!_OS 22+.
  • Fedora 42+; Rocky, AlmaLinux, RHEL, Oracle Linux or CentOS Stream 9–10.
  • openSUSE Leap 16+ or Tumbleweed; Arch, Manjaro or EndeavourOS.

Choose a vendor-maintained release. Higher OS requirements take precedence: openSUSE Leap 16 calls for more than 40 GB of disk.

More chains and concurrent invoices may need more CPU and RAM. A bigger VPS does not remove RPC quotas. Setup flows have automated tests; full fresh-VPS testing is not complete for every distribution.

Install

Point these default hostnames at your VPS, or choose your own:

  • merchant.example.com: console
  • pay.example.com: checkout
  • api.example.com: API

Use DNS-only records during setup. Open TCP 80/443 in both firewalls and keep SSH accessible. Never expose PostgreSQL or the app on ports 5432/8080.

bash <(curl -fsSL https://releases.whollycrypto.com/setup_wholly.sh)

Setup verifies downloads and configures PostgreSQL, Nginx, HTTPS and services. Prefer containers? See the optional Docker installation.

Check first or resume setup

Inspect the installer. Add --check to the command for a compatibility check without installing, or --help for options.

Resume saved progress without replacing keys or settings:

bash <(curl -fsSL https://releases.whollycrypto.com/setup_wholly.sh) --resume

Additional base domains can use the same service names. Any IPv6 records must also point to this VPS. Enable a Cloudflare proxy only after domain validation.

First-time setup

  1. Enter the HTTP Basic login. Create your admin account, choose language/timezone and review the license and privacy policy.
  2. Check Settings → Chain connections. Use healthy, independent fallback providers that support payment scanning.
  3. Create a project, then its first store.
  4. Back up the project wallets. In the store, select accepted chains and verified tokens.
  5. Set currency, confirmations, expiry, spread and tolerance. Check your credit balance before creating invoices.

QRs request the full outstanding amount. Tolerance only accepts shortfalls; confirmations still apply.

Stablecoin amounts

New recognized stablecoin quotes round up: 1.321 USDC → 1.33 USDC, even with zero tolerance. Minimum: 0.01 token.

Existing invoices, balances and partial-payment remainders stay exact. Other/custom tokens keep normal precision.

Automatic activation

Admin signup automatically registers your credit account using your email, with a one-time 10 USD equivalent welcome credit. No activation code needed.

Create an invoice. Pending activation retries automatically; reconnects never grant credit again.

Login details and defaults

Your Basic Auth login is saved in /root/whollycrypto/config/setup-credentials.txt. It is separate from the console email/password.

New projects inherit system currency and timezone defaults; stores use the project currency. Existing account timezones are managed separately in Settings → Account.

whollycrypto welcome shows your console URL.

Wallets & tokens

On first install, the first 250 CoinGecko tokens become available before the remaining catalog loads. See progress in Settings → Tokens.

Under Project → Wallets → Backup all, choose plain TXT or searchable offline HTML with QR codes. Both contain unencrypted keys and recovery phrases. Keep a private copy off the VPS; never share or upload it.

Root can access keys. Wallet recovery guides explain invoice addresses, native gas and external Monero/Lightning backups.

Add a custom token
  1. Open Store → Payment methods, select a supported EVM chain or Solana, then choose Custom token beside the search.
  2. Enter the contract or mint, name and ticker. Choose a fixed USD price or automatic DEX pricing.

The chain and decimals are checked. Prices are shared across the project's stores; existing invoices keep their original quote. A catalog match alone does not make a token ready for checkout.

Automatic pricing

DEX Screener finds exact-contract pools, including Uniswap and PancakeSwap. A pool needs at least $10,000 liquidity and a trade within the last hour. Rates refresh every minute; failed checks or prices older than five minutes block new quotes.

DEX prices can be manipulated. Use trusted standard ERC-20 or classic SPL tokens; verification is not a security audit. Token-2022/extensions are not supported. Use a fixed price if no suitable pool exists.

Currency & timezone

Settings → System → Regional defaults sets new-project currency/timezone and new-account timezone. Existing settings and credits stay unchanged; dashboard currency remains overridable.

Personal timezone: Settings → Account → Edit user.

Create an invoice

Use Payment methods → Choose for this invoice to narrow accepted chains/tokens. All store payment methods keeps the defaults.

  1. Open Project → Invoices → Create invoice.
  2. Choose the store, fiat currency and amount.
  3. Share the checkout link. Your customer chooses an available chain/token and sees the QR, remaining amount, expiry and confirmations.

Test a small payment on every method before going live. Check the invoice before fulfilling an order: a return URL is not proof of payment.

For integrations, verify signed IPN/webhooks and confirm status through the invoice API. Treat duplicate events as the same event.

API: filter payment_methods by chain_slug and asset_tickers. Inactive choices are ignored; no matches means store defaults. Chain-only includes all active assets. Shared tickers need asset IDs. Readiness checks still apply.

Amounts and confirmations

Invoice cannot be created? Check the chain's scanner badge, not just node health. TRON needs two independent tron-indexer providers. The invoice form lists unavailable methods and fixes; Wallets separates receiving checks from balances and sending gas.

API/MCP errors include error.details.payment_methods. SDK 2.4.0 adds safe explanations: PHP getPaymentMethodIssues(), Python payment_method_issues, Node paymentMethodIssues. Error reference →

Crypto quotes round upward and include the store's spread. A fiat-priced invoice does not convert the received crypto into a bank balance.

Zero confirmations settles on detection, without network-confirmation protection. Native EVM payments are detected as direct transfers. Review unusual or uncertain payments under Needs attention.

Zero-amount invoices

Blocked by default. Enable Stores → Invoice → Allow zero-amount invoices to allow manual/API zero totals. They complete immediately, without a payment, receiving address, transaction or processing fee.

Checkout

Store → Checkout: colors, logos, methods, links, Intro and Outro with font sizes. New stores follow the default store. Changes autosave.

Hosted previews never accept payments. No custom HTML/CSS/JavaScript. Valid payment choices are remembered.

Try the demo.

IPN & webhooks

IPN sends every invoice event to the store URL or invoice ipn_url. Webhooks send selected events. Both POST the same JSON snapshot.

Fulfil when status = settled. processing means payment is detected, but the accepted amount or confirmations are still missing. amount_status = paid alone is not settlement.

Statuses and callback data
Invoice statusMeaning
newAwaiting payment
processingPartial payment or awaiting finality
settledAccepted under invoice rules, or manually
expiredDeadline passed; late monitoring can continue
invalidPayment needs review or was rejected
cancelledCancelled, not refunded

Events: invoice.created, payment.received, invoice.processing, invoice.settled, invoice.expired, invoice.invalid, invoice.cancelled. Not every invoice passes through every state. Version 2 includes signed event_type, event_id and project/store IDs.

The body contains invoice_id (public UUID), status, amount_status, timing_status, resolution, sequence, amount, currency and order_id. Amounts are decimal strings: a €49.90 invoice paid in USDC still sends "amount": "49.9", "currency": "EUR".

Underpaid: amount_status = partial. Overpaid: overpaid. Late: timing_status = late. These are separate from invoice status. paid includes tolerance.

payment_info has exact amounts, transfers and chain/token identities. Customer data and metadata are also included privately.

Settlement identifies paid_chain, paid_asset, paid_payment_method_id and settlement_exchange_rate (asset units per invoice currency unit, before spread). Unproven settlements and unknown historical data stay null. Retries preserve the snapshot.

5.0.1 adds paid_asset_amount (full crypto quote) and paid_asset_amount_received (actual receipts at settlement). Exact decimal strings preserve tolerated shortfalls: "100" requested, "99" received. They stay frozen; older snapshots return null. Later receipts appear in payment_info.

Missing is not unconfirmed. remaining_amount accounts for tolerance. Never resend unconfirmed funds or combine different assets. Preserve exact decimal/atomic strings.

Quote, spread, tolerance and rounding stay locked. Market snapshots are advisory. For truncated histories, use the paginated payments API.

Signing secrets, retries and delivery history

Separate signing secrets: Store → IPN signs IPN deliveries, including an invoice's custom ipn_url. Each endpoint in Store → Webhooks has its own secret, shown when created or rotated.

Both use Wholly-Signature and the same SDK verifier, but need the matching secret, not an API key. Rotating the IPN secret does not change webhook secrets.

Verify the raw body and timestamp, match project/store scope, save durably, then return HTTP 2xx. Re-fetch the invoice from your configured API host and match your order before fulfilment. Event/delivery headers are unsigned; version 2 also puts the event identity inside the signed body.

For an order-state inbox, deduplicate by project, invoice ID and sequence. Different event types may share a revision: compare the original invoice-state fields, not whole JSON bodies. If processing every event, use the signed event ID and still fulfil each order only once.

IPN retries retryable failures up to eight times; webhooks can disable retries. Delivery may be delayed, duplicated or out of order. Never overwrite a newer invoice revision with an older one.

Search Store → IPN / Webhooks → History by order, invoice ID or email. Details shows the saved body and delivery result. Histories also appear in invoice details.

data.invoice_id matches callbacks. SDK 2.1.0 supports version 2 and legacy events; update strict receivers. Saved payloads expire after 90 days. Resend is not proof of success. Low credits pause deliveries, not payments.

Full payload, event table and receiver examples →

Exchange conversion

Optionally send supported assets to Kraken, Binance or Coinbase, then keep the coin or market-convert the credited deposit. Fiat stays on the exchange; this is not a bank payout.

Connect and choose a route
  1. In Settings → Exchanges, connect a dedicated API key, check balances and grant projects. Enable spot trading only if needed. No withdrawal permissions; restrict keys to your VPS IP.
  2. In Project → Sweep, verify the asset, deposit network and address. Token routes may require explicit contract confirmation.
  3. For each chain/token, choose either another wallet or one exchange. Select a direct market or keep the coin. Test a small transfer first.

Markets refresh about every two minutes. Stale markets block conversion. Only matched, credited deposits are used; uncertain orders are checked, never blindly resubmitted. A 1% source reserve stays at the exchange for fees and rounding, separate from processing credits.

Exchange custody, KYC, regional limits and trading/network fees apply. No multi-hop trades, leverage or bank withdrawals. Insufficient processing credit pauses new conversions, but submitted orders continue to be checked.

Processing credits

You still receive payments when credits run out. Existing stores keep invoices, checkout and payment monitoring. A missing verified billing connection or account suspension is different and can block new invoices.

Still availablePaused without usable credit
Invoices, checkout and confirmationsIPN/webhooks, including retries
Wallets, balances and backupsSweep: sends, gas, refunds and new exchange conversions
Reports, read APIs and existing settingsCreating projects and stores
Update checks and interrupted-update recoveryInstalling new versions

Top up through the credit icon. After verified recovery, enabled automation resumes, including sweep rules that send funds. Disable rules you do not want to resume.

Fees and restoring full access

The default 1% uses the settled invoice's original fiat value. Customer crypto payments are not split.

Fees continue accruing into a negative balance. Clear the negative amount and restore usable credit above your account's grace threshold. Balance changes normally appear within 10–15 seconds on a healthy connection.

Installing updates requires verified credit above zero, even with an automation grace allowance. Top-ups never install updates automatically.

Paused notifications consume no attempts. Retained deliveries resume; normal expiry still applies. Reconcile missed events through the invoice API.

If the operator disables processing fees, no new processing fees or credit restrictions apply. Previous charges remain in history. An unpaired installation can configure projects/stores, but needs verified billing to issue invoices. Already issued invoices remain monitored.

Reports

The dashboard icon before the project list combines the projects you may access. Filter by project, store, currency, timezone or date. Use today/week/month presets or a custom range up to 366 days; export store results as CSV.

How report figures work
  • Revenue shows settled invoice values before fees/refunds, using settlement dates. It is not wallet balance or profit.
  • Conversions use current cached rates, not historical accounting rates. Original currency totals remain available if rates are missing.
  • Invoice-status charts use invoice creation dates. Needs-attention figures show current unresolved issues.

AI assistants · MCP

Opt-in, project-scoped access at api.example.com/mcp. Read payments or approve invoice creation. MCP setup & permissions →

SDKs

Use our official SDKs to create invoices, check payments and verify IPN/webhooks.

PHP SDK

PHP 7.4+

Add crypto payments to your PHP app with Composer.

composer require whollycrypto/php-sdk
GitHub & examples ↗

Python SDK

Python 3.10+

Connect your Python app or backend with no runtime dependencies.

python -m pip install whollycrypto
GitHub & examples ↗

Node.js SDK

Node.js 22+

JavaScript or TypeScript, one npm package with built-in types.

npm install whollycrypto
GitHub & examples ↗

Use your installation’s API domain and keep API keys server-side. All SDKs are MIT licensed.

Domains

Open Settings → System: edit domains, check DNS & review, then activate. Old addresses stay active until validation succeeds.

HTTPS is automatic. Toggle Cloudflare proxy after setup without changing Wholly Crypto settings. Let's Encrypt stays active.

Cloudflare checklist
  • Use Full (strict), not Flexible; no Cloudflare API key needed.
  • Bypass caching and browser/bot challenges for console, checkout and API.
  • Allow /.well-known/acme-challenge/ on port 80 without forced redirects or access challenges for renewals.

Checks query authoritative DNS and verify the origin; results show Direct or Cloudflare. Visitors may temporarily cache older DNS.

Account security

Set up 2FA under Settings → Account. Project-only users use My security in the footer. Confirm your password, scan the TOTP QR/key and enter a six-digit code.

Try Verifyr Authenticator for iOS or Android, or another standard TOTP app. Save the one-use recovery codes separately from your password. Keep HTTP Basic protection enabled.

Recovery and appearance

Replacing codes or disabling 2FA needs your password and an authenticator or unused recovery code. Other sessions are signed out. Keep phone/server clocks synchronized; 2FA does not secure a compromised server.

The theme icon cycles Light → Dim → Dark and remembers this browser's choice. Store checkout themes are separate.

Server security

  • Use SSH keys and restrict SSH to trusted IPs. Keep a tested recovery connection.
  • Enable Cloudflare proxy after setup, using Full (strict).
  • In Settings → System → Source IP restrictions, allow trusted IPs/ranges per live merchant.* or api.* hostname. Current console IP must stay allowed.
  • Normally leave pay.* open: customers need to pay from anywhere.
Cloudflare and lockout recovery

Optionally add Cloudflare access rules. Exempt HTTPS renewal challenges; avoid interactive login challenges on API and checkout. Proxy mode alone is not an IP restriction.

Locked out? Run whollycrypto access-reset --domain merchant.example.com over SSH. Only that hostname reopens, within five seconds; account authentication stays enabled.

Fix HTTPS

whollycrypto ssl
whollycrypto ssl --fix

The first command checks active certificates. --fix repairs TLS support files and renews missing/expiring certificates. Nginx must be running; keep ports 80/443 open.

Reissue one certificate

Run whollycrypto ssl --domain merchant.example.com --reissue for a forced replacement. Issuance limits apply. If Cloudflare blocks the HTTP challenge, temporarily use DNS-only. Wallets, authentication and domains stay unchanged.

Daily checks

  • Review Needs attention for underpaid, overpaid, late, reorged or ambiguous payments and failed notifications.
  • Monitor disk space, scanner lag and independent node fallbacks. Public endpoints have no capacity guarantee.
  • Keep Linux current, restrict SSH and scope users/API keys to the required projects and permissions.

Exception decisions need a reason. A refund is a separate confirmed transfer; changing invoice status does not send money.

Updates & backups

Read the release notes, then use Settings → System → Software updates or the CLI. Updates verify signatures, create backups and check health. Checkout pauses during installation.

whollycrypto update --check
whollycrypto update

From 0.1.32, updates wait two minutes for background jobs. Checkout stays online during this wait. Timeouts cancel updates and restore timers.

Upgrading from 0.1.31 or earlier

Older updater blocked? Run this once. Your installed key verifies the helper.

bash <(curl -fsSL https://releases.whollycrypto.com/update_wholly.sh)

Keep tested off-server backups of PostgreSQL, configuration, wallet encryption keys and wallet exports. Local recovery archives are unencrypted.

Restore matching database/configuration backups together. Never interrupt migrations or run older binaries against newer databases. Credit rules still apply.

GitHub download fallback

Setup and updates automatically try the official GitHub mirror. Signature, checksum and credit checks remain required.

Server CLI

Root commands over SSH.

whollycrypto status
whollycrypto doctor
whollycrypto backup
All commands
CommandPurpose
whollycrypto versionInstalled version.
whollycrypto statusApp and database health.
whollycrypto logsLast 80 app log lines.
whollycrypto restartRestart and check health.
whollycrypto doctorRead-only installation checks.
whollycrypto doctor --fixRepair managed permissions and a missing CLI link.
whollycrypto htaccessReset a Basic Auth login.
whollycrypto admin-resetReset an administrator password.
whollycrypto 2fa-resetReset one account’s authenticator without changing its password.
whollycrypto transfers status
whollycrypto transfers enable
whollycrypto transfers disable
Check, enable or pause sending for all projects. Enable restarts the app and can run saved rules immediately; confirmation is required. Updates preserve pauses.
whollycrypto sslCheck HTTPS; add --fix to repair.
whollycrypto access-reset --domain HOSTRemove one hostname's IP restriction.
whollycrypto update --checkCheck for signed updates.
whollycrypto updateBack up, update and restart.
whollycrypto backupRoot-only archive in /root/whollycrypto/backups/releases/.
whollycrypto recoverRecover an interrupted update with an unchanged schema. Not a general restore.
whollycrypto welcomeConsole URL and links.
whollycrypto --helpCommands; add --help after one for its options.
Diagnose installation problems

Checks are read-only, with a non-zero exit when attention is needed. --fix only repairs managed permissions and a missing CLI link. It never changes billing, deletes data or restarts services.

Reset the Basic Auth login
whollycrypto htaccess

Select a user, choose a name/password and confirm. This resets the browser's first login prompt, not your console account. Other users and API credentials stay unchanged.

Reset an administrator password
whollycrypto admin-reset

Choose an admin by email and confirm the new password. Sessions and account login locks are cleared; IP restrictions remain.

Normal resets keep 2FA. If both authenticator and recovery codes are lost, explicitly run whollycrypto admin-reset --email admin@example.com --reset-2fa. Set up 2FA again after login.

Needs the database, not the app or old password. Other accounts, wallets and permissions stay unchanged; disabled accounts stay disabled.

Reset 2FA

Lost both authenticator and recovery codes? Choose an account as root.

whollycrypto 2fa-reset
# Or select the account directly:
whollycrypto 2fa-reset --email user@example.com

Removes sessions, authenticator and recovery codes. Log in with the existing password and configure 2FA again. Permissions, wallets and Basic Auth stay unchanged; disabled accounts stay disabled.

Requires the database, not the app or credits. Non-interactive: --email and --yes.

Generated passwords and automation

Enter generates a password in /root/whollycrypto/config/credential-resets/: private, unencrypted, usable after success. Manually entered passwords aren't saved.

whollycrypto htaccess --user admin --new-user operator
whollycrypto admin-reset --email admin@example.com

Without a terminal, specify the existing account plus --generate or --password-file /root/private-password.txt, and --yes. Password files must be root-owned, mode 600. Never put passwords in commands or environment variables.

--yes skips confirmation only. Finish interrupted-update recovery first; concurrent account changes stop resets.