Repository navigation
feat: phone credit (CoinPay), 71 countries, agents on the phone, qc call/phone (0.15.0, qc 0.10.0) - #336
Conversation
…all/phone (0.15.0, qc 0.10.0) Anthony: "do it all" (paid credit, international, agents placing calls, qc). - Credit: $5/$10/$25 on CoinPay's hosted checkout (business "qryptchat-web" made with coinpay-onboard; keys in vault + app.env). POST /api/phone/topup -> phone_topups row + checkout session; /api/phone/coinpay-webhook verifies the signature and credits exactly once (phone_credit_topup), by metadata or session. - Billing in micro-dollars: US/Canada free minutes first, then 1 cent per started minute, charged at hang-up; other countries 2x the carrier cost read from Telnyx detail records (telnyx_session_id), settled when the records exist (retried, and on GET /api/phone). phone_charge_call charges once. Integer micros (2 * 0.0072 * 1e6 is 14400.000000000002 in floating point). - International: 71 countries on the Telnyx profile with max_destination_rate 0.10, libphonenumber-js parsing, premium/shared-cost/UAN refused, no free minutes abroad, allowance limited at the worst-case 20.4 cents/min. - Agents: Call Control app "qrypt.chat agent calls" posts to /api/phone/agent-webhook, stored in phone_agent_events; the agent host long-polls /api/phone/agent-events (AGENT_PHONE_SECRET) because it takes no inbound connections. agent-phone (on the host) transcribes with Telnyx transcription_start and speaks with ElevenLabs via a Telnyx integration secret. - qc phone (minutes, credit, recent calls) and qc call <number> (opens the dialer). - /phone: credit and buy buttons, + for international numbers, per-call charges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ThreatCrush Security Scan17 finding(s) MEDIUM: 13 | LOW: 4
Snippets are redacted; ThreatCrush never prints matched credential material. |
| const res = await fetch('/api/phone/topup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ dollars }) }); | ||
| const data = await res.json().catch(() => ({})); | ||
| if (!res.ok || !data.url) throw new Error(data.error || 'Could not start the checkout'); | ||
| window.location.href = data.url; |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Caution Review the following alerts detected in dependencies. According to your organization's Security Policy, you must resolve all "Block" alerts before proceeding. It is recommended to resolve "Warn" alerts too. Learn more about Socket for GitHub.
|
….15.1) (#337) #336 was merged with Socket's "Pull Request Alerts" check failing: the package pulled zod 3.25.76 (GHSA-mrhx-c7xc-m3w5, memory exhaustion validating oversized constrained collections, fixed in 4.6.6) plus the MCP SDK and express, all for its CLI/MCP. The app only needs three things from it, now in src/lib/server/coinpay.js with the same behaviour: createCheckoutSession, verifyWebhook (HMAC-SHA256 "t.body", 5 min), readWebhook. zod is back to 4.6.7. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Anthony: "do it all": paid credit, international calling, agents placing calls, and qc.
Credit (CoinPay)
coinpay-onboard site qryptchat-web: every step ✓ (business made, 16 payout wallets copied, webhook pointed at/api/phone/coinpay-webhook). The fourCOINPAY_*keys are in the vault and on dev2.POST /api/phone/topup {dollars: 5|10|25}creates aphone_topupsrow and a hosted checkout (any coin or PayPal) whose metadata names the row. The webhook verifiesx-coinpay-signatureand credits exactly once (phone_credit_topup), matching by metadata or, failing that, by checkout session.Billing, in micro-dollars
telnyx_session_id, acostper leg). These appear a minute or so later, so settlement is retried 90 s / 5 min / 30 min after the call and also runs onGET /api/phone.phone_charge_callcharges once. Measured real costs: US phone leg $0.002/min in whole minutes, browser leg $0.002/min.2 * 0.0072 * 1e6is14400.000000000002in floating point, which over-charged by one micro before the fix.International
libphonenumber-jsparsing. Premium-rate, shared-cost and UAN numbers are refused.Agents on the phone
/api/phone/agent-webhookand intophone_agent_events./api/phone/agent-eventswithAGENT_PHONE_SECRET.agent-phone(dev2) uses Telnyxtranscription_startplusspeakwith ElevenLabs voices (integration secretqrypt_elevenlabs). Its first line says it's an AI assistant calling for Anthony.qc 0.10.0:
qc phone(free minutes, credit, recent calls) andqc call <number>(opens the dialer prefilled via/phone?to=).Already applied on dev2: migrations
20261026150000_phone_credit.sqland20261026160000_phone_agent_events.sql(existing calls backfilled as settled), plus envCOINPAY_*,AGENT_PHONE_SECRETandTELNYX_AGENT_APP_ID(app.env backups bak-010/011, vault).bun run test:cipasses 841/841. New tests cover rules (international parsing, allowance, charges) and phone (free US call, paid US minutes, international settlement from records, refusals, operators, dial failure, top-up credited once, top-up by session).🤖 Generated with Claude Code