Skip to content

docs: add NowPayments integration plan (v1 top-ups) - #376

Draft
gianpaj wants to merge 2 commits into
mainfrom
claude/add-nowpayments-integration-HyvHg
Draft

docs: add NowPayments integration plan (v1 top-ups)#376
gianpaj wants to merge 2 commits into
mainfrom
claude/add-nowpayments-integration-HyvHg

Conversation

@gianpaj

@gianpaj gianpaj commented May 14, 2026

Copy link
Copy Markdown
Owner

Summary

Add comprehensive integration plan for NowPayments (crypto payments) as a second payment provider alongside Stripe. This document outlines the architecture, locked-in decisions, and implementation roadmap for v1 (one-time top-ups) and v2 (crypto subscriptions).

Changes

  • Added docs/specs/nowpayments-integration.md with complete integration specification including:
    • Goal and context recap of existing Stripe infrastructure
    • Key differences between NowPayments and Stripe (no card-on-file, hosted invoices, HMAC-SHA512 webhooks, status state-machine)
    • Locked-in architectural decisions (provider column in DB, v1 scope, UI tabs, accepted coins)
    • Detailed v1 implementation plan: DB migrations, pricing config reuse, new library files, top-up flow, webhook security, idempotency guarantees
    • Environment variable requirements
    • Testing strategy
    • Implementation order and v2 scope deferral
    • Open risks and mitigation notes

How to test

This is a specification document; no code changes or testing required. The plan is ready for implementation once approved.

Scope

  • Docs

Checklist

  • I self-reviewed this PR
  • I updated docs/comments where needed

Notes for reviewers

This is a locked-in design document (decisions finalized 2026-05-13). It serves as the blueprint for the actual implementation PR. Key points for review:

  • v1 scope is top-ups only — crypto subscriptions deferred to v2 due to JWT auth complexity
  • Idempotency strategy — composite unique (provider, reference_id) at DB level + pending row inserted before redirect
  • Webhook security — HMAC-SHA512 over sorted JSON keys (NowPayments quirk); raw body read before parsing
  • Sandbox risk — noted that NowPayments sandbox IPN timing can differ; production smoke test recommended before launch
  • Stuck pending invoices — acknowledged as low-risk but noted for future cleanup job

https://claude.ai/code/session_01Jkmvx86vnQER8fwzR1rym7

Captures the locked-in v1 scope (crypto top-ups alongside Stripe), DB
provider column strategy, IPN webhook handling, and deferred v2
subscription work.
Copilot AI review requested due to automatic review settings May 14, 2026 09:09
@bolt-new-by-stackblitz

Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@vercel

vercel Bot commented May 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sexyvoice Ready Ready Preview, Comment Jul 2, 2026 9:56pm
sexyvoice-docs Ready Ready Preview, Comment Jul 2, 2026 9:56pm

Request Review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a detailed integration plan for adding NowPayments as a crypto payment provider, covering database schema updates, webhook handling, and UI changes. The feedback focuses on technical implementation details necessary for a robust system, including ensuring database atomicity for credit grants, strictly enforcing idempotency in webhooks to prevent duplicate transactions, and resolving logic inconsistencies regarding underpayments. Additionally, the reviewer pointed out technical requirements for HMAC signature verification, such as specific JSON serialization rules and the correct usage of Buffers with timingSafeEqual, while also noting the need to explicitly link the currency whitelist to environment variables and handle credit decrements during refunds.

Comment thread docs/specs/nowpayments-integration.md Outdated
Comment on lines +127 to +129
- `finished` → atomically look up the pending row by `(provider='nowpayments',
reference_id=order_id)`, set `amount = credits` and
`metadata.status = 'finished'`, then call `increment_user_credits` RPC.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To ensure idempotency, the webhook handler must verify that the transaction hasn't already been processed before calling increment_user_credits. Since webhooks can be redelivered, checking if the existing record's metadata.status is already finished is necessary to prevent duplicate credit grants.

Comment thread docs/specs/nowpayments-integration.md Outdated
Comment on lines +127 to +129
- `finished` → atomically look up the pending row by `(provider='nowpayments',
reference_id=order_id)`, set `amount = credits` and
`metadata.status = 'finished'`, then call `increment_user_credits` RPC.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The update to the credit_transactions table and the call to the increment_user_credits RPC should be performed within a single database transaction. This ensures that credits are not granted without a corresponding record update, and vice versa, maintaining data consistency in case of partial failures.

Comment thread docs/specs/nowpayments-integration.md Outdated
- `finished` → atomically look up the pending row by `(provider='nowpayments',
reference_id=order_id)`, set `amount = credits` and
`metadata.status = 'finished'`, then call `increment_user_credits` RPC.
- `partially_paid` → update metadata, Sentry breadcrumb, no credit grant.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This line contradicts the earlier statement in line 45 which suggests credits might be granted for partially_paid if the amount meets the expected value. The integration plan should define a consistent policy for handling underpayments.

Comment thread docs/specs/nowpayments-integration.md Outdated
- Recursively sort all object keys (NowPayments quirk — applies to nested
objects and arrays-of-objects).
- `crypto.createHmac('sha512', IPN_SECRET).update(sortedJson).digest('hex')`.
- Compare to `x-nowpayments-sig` via `crypto.timingSafeEqual`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

crypto.timingSafeEqual requires both inputs to be Buffer objects of the same length. The implementation should convert the hex strings to Buffers and handle potential length mismatches to avoid runtime errors.

Comment thread docs/specs/nowpayments-integration.md Outdated
Comment on lines +238 to +240
- **Refunds.** NowPayments refunds are manual. If we see `payment_status:
refunded` after we've credited, we need to insert a compensating
`refund`-type row. The enum already supports `refund` — handler stub is

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The plan should specify that the decrement_user_credits RPC must be invoked when a refund is processed. Simply logging the transaction in credit_transactions will not update the user's balance in the credits table, leading to a state mismatch.

Comment thread docs/specs/nowpayments-integration.md Outdated
Comment on lines +157 to +159
- Recursively sort all object keys (NowPayments quirk — applies to nested
objects and arrays-of-objects).
- `crypto.createHmac('sha512', IPN_SECRET).update(sortedJson).digest('hex')`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The HMAC calculation depends on the exact string representation of the JSON. The plan should specify the serialization rules (e.g., no whitespace, specific character encoding) to ensure the generated hash matches the one sent by NowPayments.

Comment thread docs/specs/nowpayments-integration.md Outdated
Comment on lines +119 to +120
- `pay_currency` whitelist (optional, hosted page lets user pick from
enabled coins)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The plan should explicitly mention that the pay_currency whitelist in the POST /v1/invoice call should be populated using the NOWPAYMENTS_PAY_CURRENCIES environment variable defined in section 8.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a design/specification document describing how to integrate NowPayments as a second payment provider alongside Stripe, with v1 focused on one-time crypto top-ups and v2 deferring crypto “subscriptions”.

Changes:

  • Introduces a detailed NowPayments integration plan (architecture, DB changes, webhook security, idempotency, UI changes, env vars, testing, rollout order).
  • Documents key behavioral differences vs Stripe (hosted invoices, IPN webhook signature scheme, payment status state machine).
  • Captures “locked-in” product/UX decisions for v1 and explicitly defers v2 items.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/specs/nowpayments-integration.md Outdated
Comment on lines +25 to +27
- **DB:** `credit_transactions` (enum `purchase|usage|freemium|topup|refund`,
plus `reference_id`, `subscription_id`, `metadata JSONB`); `profiles.stripe_id`
links the Supabase user to a Stripe customer.
Comment thread docs/specs/nowpayments-integration.md Outdated
Comment on lines +69 to +72
- Drop existing `credit_transactions_reference_id_idx`.
- Add composite uniqueness:
`UNIQUE (provider, reference_id) WHERE reference_id IS NOT NULL`.
- Backfill not needed (default takes care of existing rows).
Comment thread docs/specs/nowpayments-integration.md Outdated
Comment on lines +108 to +112
- Generate `order_id = "topup_${userId}_${nanoid()}"`.
- Insert a *pending* row in `credit_transactions`:
`{ provider: 'nowpayments', reference_id: orderId, amount: 0,
type: 'topup', metadata: { status: 'pending', packageId, credits,
dollarAmount } }`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants