import { sdk } from '../sdk' import { configFile } from '../file-models/config.json' const { InputSpec, Value } = sdk // Connects the relay to the operator's Zaprite account so users can buy a // prepaid Pro/Max period with a CARD (the "Pay by card" rail alongside the // Bitcoin/BTCPay rail). Leave the API key blank to disable the card rail — // the app hides "Pay by card" and the Bitcoin rail keeps working. // // To get your Zaprite API key: // 1. Zaprite → Settings → API → create a key // 2. Paste it below (stored masked) // // To set up the webhook in Zaprite: // 1. Zaprite → Settings → Webhooks → add endpoint // 2. URL: https:///relay/zaprite/webhook // 3. Subscribe to order paid / completed events // (No webhook secret is needed — the relay re-fetches each order from // Zaprite's authenticated API to confirm payment before granting.) // // Card prices are charged in the currency below (cents for USD). They're // separate from the dashboard's "Set Tier Prices (USD)" accounting figure; // these are the real amounts a card buyer pays. Default ≈ parity with the // sat prices ($21 / $42) — raise them to add a premium for card fees. const inputSpec = InputSpec.of({ relay_zaprite_api_key: Value.text({ name: 'Zaprite API Key', description: 'API key from Zaprite → Settings → API. Used to create hosted card checkouts and to re-fetch orders for webhook verification. Leave blank to disable the card rail.', required: false, default: null, masked: true, minLength: 0, maxLength: 256, }), relay_zaprite_base_url: Value.text({ name: 'Zaprite Base URL', description: 'Zaprite API base URL. Leave as the default unless Zaprite tells you otherwise.', required: false, default: 'https://api.zaprite.com', minLength: 0, maxLength: 256, patterns: [ { regex: '^(https?://.+)?$', description: 'Must be empty or start with http:// or https://', }, ], }), relay_zaprite_currency: Value.text({ name: 'Card Currency', description: 'Fiat currency the card is charged in (ISO code, e.g. USD, EUR). The card prices below are in this currency.', required: false, default: 'USD', minLength: 0, maxLength: 8, }), pro_card_price: Value.number({ name: 'Pro — Card Price', description: 'Amount a card buyer pays for one prepaid Pro period, in the card currency. Default ≈ parity with the 21,000-sat Bitcoin price.', required: true, default: 21, min: 0, max: 100_000, integer: false, step: 0.01, placeholder: null, }), max_card_price: Value.number({ name: 'Max — Card Price', description: 'Amount a card buyer pays for one prepaid Max period, in the card currency. Default ≈ parity with the 42,000-sat Bitcoin price.', required: true, default: 42, min: 0, max: 100_000, integer: false, step: 0.01, placeholder: null, }), }) export const setZapriteConnection = sdk.Action.withInput( 'set-zaprite-connection', async ({ effects }) => ({ name: 'Set Zaprite Connection (card purchases)', description: 'Wire the relay to your Zaprite account so users can buy Pro/Max with a card. Leave the API key blank to disable the card rail — the Bitcoin rail keeps working without it. Remember to add the webhook in Zaprite pointing at https:///relay/zaprite/webhook', warning: null, allowedStatuses: 'any', group: 'Tiers', visibility: 'enabled', }), inputSpec, async ({ effects }) => { const config = await configFile.read().once() let cents: any = {} try { cents = JSON.parse(config?.relay_tier_prices_fiat_cents_json || '{}') } catch { cents = {} } const toMajor = (c: any, fallback: number) => typeof c === 'number' && Number.isFinite(c) ? c / 100 : fallback return { relay_zaprite_api_key: config?.relay_zaprite_api_key || undefined, relay_zaprite_base_url: config?.relay_zaprite_base_url || 'https://api.zaprite.com', relay_zaprite_currency: config?.relay_zaprite_currency || 'USD', pro_card_price: toMajor(cents?.pro, 21), max_card_price: toMajor(cents?.max, 42), } }, async ({ effects, input }) => { // Card prices are entered in major units (dollars) but Zaprite + the // relay charge in the currency's smallest unit (cents), so ×100. const fiatCents = { pro: Math.round(Number(input.pro_card_price ?? 21) * 100), max: Math.round(Number(input.max_card_price ?? 42) * 100), } await configFile.merge(effects, { relay_zaprite_api_key: (input.relay_zaprite_api_key || '').trim(), relay_zaprite_base_url: ( input.relay_zaprite_base_url || 'https://api.zaprite.com' ).trim(), relay_zaprite_currency: (input.relay_zaprite_currency || 'USD') .trim() .toUpperCase(), relay_tier_prices_fiat_cents_json: JSON.stringify(fiatCents), }) return null }, )