Skip to content

fix(payments): use the real CoinPay API, every coin plus PayPal - #122

Merged
ralyodio merged 1 commit into
mainfrom
fix/coinpay-real-api
Oct 21, 2026
Merged

ralyodio merged 1 commit into
mainfrom
fix/coinpay-real-api

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

The checkout called POST /v1/invoices on api.coinpayportal.com, a host and endpoint that do not exist, so no order could be paid.

  • Rails: CoinPay hosted checkout sessions (POST /api/checkout/sessions) when available, feature-detected (404 = not yet, rechecked every 10 min) or forced via COINPAY_CHECKOUT_SESSIONS. Fallback: a picker over every coin the business has a wallet for (GET /api/supported-coins) via POST /api/payments/create, plus PayPal via POST /api/paypal/payments/create.
  • Metadata on every payment: property, account_id, email, order_id. The CoinPay id is stored on the order as <rail>:<id>, because crypto confirmations and PayPal events carry only the payment id.
  • Webhook: X-CoinPay-Signature: t=..,v1=.., nested payload, order found by stored ref then metadata.order_id, unknown orders acked 200.
  • Checkout page is live (it said "not open yet" although the catalog says available); /checkout/success and /checkout/cancel added.
  • COINPAY_API_BASE normalised (/api suffix and the dead api. host fold onto https://coinpayportal.com), so prod env needs no change.

Tests: vitest run 485/485, bun run typecheck clean, prettier clean.

🤖 Generated with Claude Code

The checkout called POST /v1/invoices on api.coinpayportal.com, a host and
an endpoint that do not exist, so no order could ever be paid.

- createCheckout uses CoinPay hosted checkout sessions when the endpoint
  exists (feature-detected, COINPAY_CHECKOUT_SESSIONS=auto|on|off), else
  POST /api/payments/create for a coin the business holds a wallet for
  (GET /api/supported-coins) or POST /api/paypal/payments/create.
- Every rail carries metadata {property, account_id, email, order_id}; the
  CoinPay id is stored on the order as <rail>:<id> because crypto
  confirmations and PayPal events carry only the payment id.
- Webhook verifies X-CoinPay-Signature t=..,v1=.., reads the nested payload
  (data.payment_id, not the event id), maps the order by stored ref then
  metadata.order_id, and acks unknown orders with 200.
- Checkout page is live: coin picker plus PayPal; /checkout/success and
  /checkout/cancel pages exist.
- Cron reconciliation reads GET /api/payments/:id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ralyodio
ralyodio merged commit 31b0d1e into main Oct 21, 2026
5 checks passed
@ralyodio
ralyodio deleted the fix/coinpay-real-api branch October 21, 2026 12:08
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

1 finding(s)

MEDIUM: 1

Severity Rule Location
MEDIUM js-unescaped-html-sink apps/site/app/_components/pit-footer.tsx:17

Snippets are redacted; ThreatCrush never prints matched credential material.

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.

1 participant