From f92f912be54b75860aac229808d48fdfd7e9d975 Mon Sep 17 00:00:00 2001 From: sirily Date: Tue, 10 Mar 2026 16:14:36 +0300 Subject: [PATCH] docs: choose BTCPay for payments --- .../adr-0001-payment-processor-selection.md | 30 ++++ docs/ops/deployment.md | 5 + docs/ops/payment-provider-selection.md | 134 ++++++++++++++++++ docs/plan/system-plan.md | 3 +- 4 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 docs/architecture/adr-0001-payment-processor-selection.md create mode 100644 docs/ops/payment-provider-selection.md diff --git a/docs/architecture/adr-0001-payment-processor-selection.md b/docs/architecture/adr-0001-payment-processor-selection.md new file mode 100644 index 0000000..0ec554a --- /dev/null +++ b/docs/architecture/adr-0001-payment-processor-selection.md @@ -0,0 +1,30 @@ +# ADR-0001: Use BTCPay Server as the primary payment processor + +## Status +Accepted + +## Context +- The product requires crypto invoice checkout with manual subscription renewal. +- `merchant noKYC` is a hard requirement. +- `crypto-to-crypto noKYC` is desirable, but it does not replace the merchant-side requirement. +- Hosted processors can offer a usable API surface, but they also introduce AML/KYC escalation risk, payout holds, and custodial exposure. +- The product already targets operator-managed infrastructure, so an additional self-hosted payment component is operationally acceptable. + +## Decision +Use `BTCPay Server` as the primary payment processor. + +Keep the application payment adapter provider-agnostic, but treat hosted processors as non-default alternatives that require an explicit policy change. + +## Rationale +- `BTCPay Server` is self-hosted and non-custodial, which fits the hard `merchant noKYC` requirement better than hosted processors. +- A self-custody path materially reduces the risk that a payment provider freezes merchant balances after receiving suspicious funds. +- The API and webhook model is sufficient for invoice creation, status reconciliation, and callback handling. +- The operational tradeoff is acceptable because the product already assumes server-managed infrastructure. + +## Consequences +- Deployment must account for a self-hosted BTCPay stack and its persistent data. +- Payment operations now include wallet, backup, and reconciliation responsibilities that a hosted processor would otherwise absorb. +- Later support for hosted processors remains possible through the shared payment adapter contract, but they are out of policy unless the `merchant noKYC` requirement changes. + +## References +- [payment-provider-selection.md](/home/sirily/nroxy/docs/ops/payment-provider-selection.md) diff --git a/docs/ops/deployment.md b/docs/ops/deployment.md index 09ce314..b058e99 100644 --- a/docs/ops/deployment.md +++ b/docs/ops/deployment.md @@ -16,6 +16,7 @@ Deploy on one VPS with Docker Compose. - `postgres`: primary database - `caddy`: TLS termination and reverse proxy - optional `minio`: self-hosted object storage for single-server deployments +- `btcpay`: self-hosted payment stack for crypto invoice checkout ## Deployment notes - Run one Compose project on a single server. @@ -23,6 +24,8 @@ Deploy on one VPS with Docker Compose. - Keep secrets in server-side environment files or a secret manager. - Back up PostgreSQL and object storage separately. - Prefer Telegram long polling to avoid an extra public webhook surface for the bot. +- Keep BTCPay persistent data and wallet material isolated from app volumes and back them up separately. +- If BTCPay requires auxiliary services, treat them as part of the payment stack rather than as app-internal services. ## Upgrade strategy - Build new images. @@ -42,7 +45,9 @@ Deploy on one VPS with Docker Compose. - provision DNS and TLS - provision PostgreSQL storage - provision S3-compatible storage or enable local MinIO +- provision BTCPay Server storage and wallet backup path - create `.env` - deploy Compose stack - run database migration job - verify web health, worker job loop, and bot polling +- verify BTCPay invoice creation, callback delivery, and payout/reconciliation procedures diff --git a/docs/ops/payment-provider-selection.md b/docs/ops/payment-provider-selection.md new file mode 100644 index 0000000..19de44c --- /dev/null +++ b/docs/ops/payment-provider-selection.md @@ -0,0 +1,134 @@ +# Payment Provider Selection + +## Status +Working selection criteria for the product payment processor. Reviewed on 2026-03-10 against official provider materials. + +## Goal +Pick a crypto payment path that fits `nproxy`: +- monthly crypto subscription +- manual renewal only +- fixed-price invoice checkout +- single-VPS deployment +- safe callback or polling-based reconciliation + +## Hard requirements +- Crypto invoice API for fixed-price checkout. +- Manual renewal flow. The provider must not force native recurring billing. +- Stable external invoice identifier for reconciliation. +- Clear invoice lifecycle that can be mapped to `pending`, `paid`, `expired`, and `canceled`. +- Webhook support or a reliable status polling API. Both is preferred. +- Idempotent event handling. Duplicate callbacks or repeated status checks must not cause repeated subscription activation. +- Metadata or reference fields so the app can correlate `userId`, local `invoiceId`, and `subscriptionId`. +- Test mode, sandbox, or another safe way to validate integration without real funds. +- Merchant documentation clear enough to implement and operate without reverse engineering. +- Fees and supported chains viable for small monthly subscription invoices. + +## noKYC requirement +`noKYC` is a hard selection criterion for the product. + +For this repository, `noKYC` means all of the following in the normal operating path: +- The merchant can start accepting payments without mandatory KYC or KYB review. +- The payer can complete checkout without mandatory identity verification. +- The provider does not require custody of merchant funds unless that is an explicit deployment choice. + +Track `crypto-to-crypto noKYC` separately from merchant onboarding: +- `merchant noKYC` asks whether `nproxy` can start operating without provider-side KYC or KYB. +- `crypto-to-crypto noKYC` asks whether a payer can complete an on-chain crypto payment from a normal wallet without entering an identity-verification flow. + +The first one is the hard gate. The second one is still valuable and should be recorded in the comparison. + +Evaluation rule: +- `pass`: official materials support operating without mandatory KYC in the normal path. +- `borderline`: marketing or docs imply low-friction onboarding, but the provider reserves the right to require KYC, hold funds, or escalate compliance checks. +- `fail`: official docs explicitly require merchant verification, or the product is clearly compliance-gated. + +## Comparison matrix + +| Provider | Model | Merchant noKYC | Crypto-to-crypto noKYC | API / webhooks | Test mode | Product fit | Notes | +| --- | --- | --- | --- | --- | --- | --- | --- | +| BTCPay Server | Self-hosted, self-custody | pass | pass | pass | pass | strong | Best fit for strict `noKYC`. Higher ops cost because the merchant runs the payment stack. | +| NOWPayments | Hosted processor | borderline | borderline to pass | pass | unclear | medium | Closest hosted option. Normal crypto-to-crypto flow appears low-friction, but official support docs reserve the right to request verification and hold suspicious transactions. | +| Cryptomus | Hosted processor | fail | unclear | pass | unclear | weak | Official help center says merchants need to pass KYC to use the platform. Reviewed materials did not give a clean official `crypto-to-crypto noKYC` promise for payers. | +| Coinbase Commerce / Coinbase Business | Hosted processor | fail | unclear | pass | pass | weak | Strong API surface, but the reviewed official materials do not support a strict `noKYC` posture. | + +## Provider notes + +### BTCPay Server +- Official BTCPay materials describe it as self-hosted and non-custodial. +- Official integration copy explicitly positions it as avoiding complicated KYC and keeping merchants in control of funds. +- This is the cleanest fit if `noKYC` must be enforced as a hard requirement rather than a preference. +- Tradeoff: the team must operate the service, wallet integration, backups, and chain support. + +### NOWPayments +- Official API materials cover invoice creation and payment-status callbacks. +- Official support materials say account verification is not required in general, but NOWPayments may still request KYC details and put transactions on hold in suspicious cases. +- For the payer side, direct crypto-to-crypto checkout looks closer to `noKYC` than card or custodial account flows, but the official materials reviewed still leave compliance-escalation risk. +- That means it does not cleanly satisfy a strict `noKYC` rule. It is only acceptable if the product can tolerate compliance escalation risk. + +### Cryptomus +- Official merchant docs cover invoice APIs and callbacks. +- Official help materials state that a merchant must pass KYC verification to use the platform. +- That merchant-side requirement is enough to fail the current hard gate even if a payer-side crypto path is relatively low-friction. +- This makes it a direct mismatch for the current product requirement. + +### Coinbase Commerce / Coinbase Business +- Official Coinbase Commerce docs cover charges, webhooks, and test integration paths. +- Official Coinbase legal materials state that identity verification is required for customers to use Coinbase services. +- The reviewed official materials did not give a clean promise that external-wallet crypto-to-crypto checkout remains `noKYC` end to end. +- This is operationally mature, but it is not compatible with a strict `noKYC` requirement. + +## Recommendation +- If `noKYC` is truly hard, prefer `BTCPay Server`. +- If a hosted processor is still desired, treat `NOWPayments` as the only current shortlist candidate from this review, but mark it as `borderline`, not `pass`. +- If the team later relaxes the hard gate from `merchant noKYC` to only `crypto-to-crypto noKYC`, rerun the comparison because the shortlist may widen. +- Do not choose `Cryptomus` or `Coinbase` while `noKYC` remains a hard requirement. + +## Merchant KYC / AML risk +This section answers the practical merchant-side risk question: can a payment path create a real loss or freeze scenario if a customer pays with suspicious or "dirty" crypto. + +### Short answer +- With a hosted or custodial processor, yes, there is a real risk of payout holds, extra KYC/KYB review, delayed settlement, or account restrictions if the provider flags a payment as suspicious. +- With a self-hosted and non-custodial path such as `BTCPay Server`, the processor itself does not hold your funds, so it cannot directly freeze coins that have already landed in your wallet. +- Even with self-custody, the risk is not gone. It moves downstream to whatever exchange, custodian, OTC desk, or banking ramp you use later. + +### Can you lose all the money? +- The official materials reviewed support a real hold and compliance-escalation risk for hosted processors, especially around suspicious transactions. +- They do not, by themselves, prove guaranteed confiscation of all funds. +- The practical risk is usually loss of access, payout freeze, account closure, or long investigation windows rather than an automatic irreversible seizure. +- If your operating balance sits inside a custodial processor or exchange account during that event, the business impact can still feel like "losing the money" for an operationally important period. + +### Why this matters for provider choice +- `NOWPayments` explicitly says verification may be requested and suspicious transactions may be put on hold. +- `Coinbase` documents identity verification and account restrictions as part of its compliance posture. +- `BTCPay Server` is self-hosted and non-custodial, so the payment processor layer is structurally less exposed to merchant-balance freezes. + +### Inference for this repository +This is an inference from the official sources above: +- If minimizing merchant-side AML/KYC freeze risk is a priority, self-custody is materially safer than a hosted custodial processor. +- Self-custody does not make tainted-funds risk disappear. It mainly removes one counterparty that could hold or block your balance before it reaches your own wallet. +- The remaining exposure shows up when funds are consolidated, swapped, or off-ramped through third parties. + +### Operational mitigations +- Prefer a non-custodial payment path. +- Keep payment receipt wallets segregated from treasury and personal wallets. +- Avoid commingling funds from unrelated customers before reconciliation. +- Keep invoice-to-transaction mapping so suspicious deposits can be isolated quickly. +- Assume any later exchange or off-ramp may apply AML screening even if the incoming payment path does not. + +## Implementation consequence +Before starting issue `#9` or a real provider integration, keep the payment adapter contract provider-agnostic: +- `createInvoice` +- `getInvoiceStatus` +- `verifyWebhook` +- `expireInvoice` or an explicit documented fallback when provider-side expiry is passive only +- correlation metadata round-trip + +## Official sources used for this review +- BTCPay Server homepage: https://btcpayserver.org/ +- BTCPay Server Greenfield API: https://docs.btcpayserver.org/API/Greenfield/v1/ +- NOWPayments API docs: https://nowpayments.io/payment-integration +- NOWPayments support on verification: https://support.nowpayments.io/hc/en-us/articles/21395546303389-Verification +- Cryptomus merchant API docs: https://doc.cryptomus.com/ +- Cryptomus KYC verification help: https://help.cryptomus.com/legal-and-security/kyc-verification +- Coinbase Commerce docs: https://docs.cdp.coinbase.com/commerce/docs/welcome +- Coinbase identity verification help: https://help.coinbase.com/en/coinbase/getting-started/verify-my-account/how-do-i-verify-my-identity-when-using-the-mobile-app diff --git a/docs/plan/system-plan.md b/docs/plan/system-plan.md index afd0bfa..3760cc6 100644 --- a/docs/plan/system-plan.md +++ b/docs/plan/system-plan.md @@ -8,7 +8,7 @@ The service hides provider-key failures behind a routed key pool. A user request ## Confirmed product decisions - One B2C website. - One monthly subscription plan. -- Crypto checkout through a payment processor. +- Crypto checkout through self-hosted `BTCPay Server`. - Manual renewal. - Text-to-image and image-to-image. - User-facing synchronous experience implemented with polling over background execution. @@ -93,6 +93,7 @@ Single VPS with Docker Compose, expected services: - `bot` - `postgres` - `caddy` or `nginx` +- `btcpay` - optional `minio` when object storage is self-hosted ## Optional extensions -- 2.49.1