@profullstack/coinpay-onboard
v0.2.0
Published
Take crypto on CoinPay, set up in one pass: Sign in with CoinPay, a business, payout wallets, webhook, keys into the vault. Library, CLI and MCP; the onboarding behind pay.agent and every fleet site launch.
Readme
@profullstack/coinpay-onboard
Take crypto on CoinPay, set up in one pass. The onboarding behind pay.agent and every fleet site launch.
One engine, idempotent (run it twice, it finds what it made):
- payout addresses on the account (optional)
- a business, found by name or made
- the account's payout wallets copied into it
- its webhook pointed where you say, and the secret
- which coins it can be paid in now
At site launch
npx @profullstack/coinpay-onboard site example.agent --webhook https://example.agent/hooks/coinpayLogs in as the merchant (COINPAY_EMAIL + COINPAY_PASSWORD, else the
profullstack-private-secrets--prod vault), then writes COINPAY_API_URL,
COINPAY_BUSINESS_ID, COINPAY_API_KEY and COINPAY_WEBHOOK_SECRET into the
site's logicsrc vault (profullstack/example-agent--prod). --no-vault prints
them instead.
In an app (Sign in with CoinPay)
import { CoinPay, onboard, verifyWebhook } from '@profullstack/coinpay-onboard'
import { authorizeUrl, exchangeCode, pkce, refresh } from '@profullstack/coinpay-onboard/oauth'
// 1. send them to CoinPay (scopes: openid profile email wallet:read merchant)
const { verifier, challenge } = pkce()
redirect(authorizeUrl({ clientId, redirectUri, state, challenge }))
// 2. back with ?code=: tokens (1 h), refresh token (30 d, single-use: keep the newest)
const tokens = await exchangeCode({ code, verifier, clientId, redirectUri })
// 3. everything they need, in one call
const coinpay = new CoinPay({ token: tokens.accessToken })
const ready = await onboard(coinpay, { businessName: 'pay.agent/alice', webhookUrl: 'https://pay.agent/hooks/coinpay' })
// 4. take a payment
const payment = await coinpay.createPayment({ businessId: ready.business.id, amountUsd: 5, currency: 'usdc_sol' })
// payment.address, payment.amountCrypto, payment.expiresAt
// 5. hear about it
verifyWebhook(rawBody, req.headers['x-coinpay-signature'], ready.webhookSecret!)Hosted checkout (any coin or PayPal)
The buyer picks the method on CoinPay's page; the site never builds a chain picker.
import { CoinPay, readWebhook, verifyWebhook } from '@profullstack/coinpay-onboard'
const coinpay = new CoinPay({ token: process.env.COINPAY_API_KEY }) // the business cp_live_ key
const session = await coinpay.createCheckoutSession({
amount: 19, // USD
description: 'Pro plan, 1 month',
customerEmail: user.email, // required
accountId: user.id, // required; use the email for guests
property: 'example.com', // the site hostname
metadata: { plan: 'pro' },
successUrl: 'https://example.com/billing?session={CHECKOUT_SESSION_ID}',
cancelUrl: 'https://example.com/pricing',
})
redirect(session.url) // https://coinpayportal.com/checkout/cs_...
// webhook
if (!verifyWebhook(rawBody, req.headers['x-coinpay-signature'], secret)) return 401
const event = readWebhook(JSON.parse(rawBody))
if (event.paid) grant(event.accountId, event.metadata, event.checkoutSessionId)readWebhook reads the nested data (the top-level id is the event, not the
payment), treats payment.confirmed and payment.forwarded as paid, and a
PayPal capture (payment.confirmed, status completed, method: 'paypal')
the same. coinpay.checkoutSession(id) polls a session; with the business key
it carries the payer fields and payment ids. With an OAuth token, pass
businessId.
CLI and MCP
coinpay-onboard site <name> --webhook URL launch step, keys into the vault
coinpay-onboard pay <business-id> <usd> [coin]
coinpay-onboard status <payment-id>
coinpay-onboard coins
coinpay-onboard mcp claude mcp add coinpay-onboard -- npx @profullstack/coinpay-onboard mcpMIT
