import { kody } from 'kody:runtime' import { listInvoices } from './invoices.ts' import { createPayout } from './payouts.ts' import { CLIENT_ID_SETUP_URL, CLIENT_SECRET_SETUP_URL, DASHBOARD_APPS_URL, LIVE_API_HOST, SANDBOX_API_HOST, clientIdSetupUrl, clientSecretSetupUrl, paypalListBalances, resolveClientIdSecretName, resolveClientSecretSecretName, type PayPalAuthInput, } from './paypal-core.ts' import { sendInvoice } from './invoices.ts' function secretEntries(result: unknown): Array<{ name?: string; scope?: string }> { if (result && typeof result === 'object' && Array.isArray((result as { secrets?: unknown }).secrets)) { return (result as { secrets: Array<{ name?: string; scope?: string }> }).secrets } return Array.isArray(result) ? result : [] } async function listUserSecretNames(): Promise> { try { const listed = await kody.secretList({ scope: 'user' }) const names = new Set() for (const entry of secretEntries(listed)) { if (entry?.name && (entry.scope === 'user' || !entry.scope)) names.add(entry.name) } return names } catch { return new Set() } } /** * Local dry-run self-check plus an optional live invoice/balance read. * * Without saved client credentials this still returns `{ ok: true, live: false }` * and the prefilled setup URLs. It never sends invoices or money. * * @example * import paypal from 'kody:@kody/paypal' * const result = await paypal({ action: 'smoke-test' }) */ export async function smokeTest(input: PayPalAuthInput = {}) { const sendPreview = await sendInvoice({ id: 'INV2-KODY-SMOKE', dryRun: true, }) if (!('dryRun' in sendPreview) || sendPreview.dryRun !== true) { throw new Error('sendInvoice dryRun self-check failed.') } const payoutPreview = await createPayout({ receiver: 'ada@example.com', amount: '1.00', currencyCode: 'USD', note: 'kody paypal smoke', dryRun: true, }) if (!('dryRun' in payoutPreview) || payoutPreview.dryRun !== true) { throw new Error('createPayout dryRun self-check failed.') } const clientIdSecret = resolveClientIdSecretName(input) const clientSecretSecret = resolveClientSecretSecretName(input) const names = await listUserSecretNames() const hasClientId = names.has(clientIdSecret) const hasClientSecret = names.has(clientSecretSecret) if (!hasClientId || !hasClientSecret) { return { ok: true, live: false, selfCheck: { dryRunSendInvoice: true, dryRunCreatePayout: true }, clientIdSecret, clientSecretSecret, setup: { auth: 'client-credentials', hosts: [LIVE_API_HOST, SANDBOX_API_HOST], dashboard: DASHBOARD_APPS_URL, nextSteps: [ 'Create a REST app at ' + DASHBOARD_APPS_URL + ' and copy the client ID and secret.', 'Save the client ID: ' + clientIdSetupUrl(clientIdSecret), 'Save the client secret: ' + clientSecretSetupUrl(clientSecretSecret), 'Approve hosts ' + LIVE_API_HOST + ' and ' + SANDBOX_API_HOST + ' on both secrets.', ], }, } } const [invoices, balances] = await Promise.all([ listInvoices({ ...input, pageSize: 3 }), paypalListBalances(input).catch((error: unknown) => ({ skipped: true, reason: error instanceof Error ? error.message : String(error), })), ]) return { ok: true, live: true, selfCheck: { dryRunSendInvoice: true, dryRunCreatePayout: true }, clientIdSecret, invoiceList: { total_items: invoices.total_items, sampleCount: invoices.items.length, sample: invoices.items, }, balances, setup: { auth: 'client-credentials', hosts: [LIVE_API_HOST, SANDBOX_API_HOST], clientIdUrl: hasClientId ? null : CLIENT_ID_SETUP_URL, clientSecretUrl: hasClientSecret ? null : CLIENT_SECRET_SETUP_URL, }, } } export default smokeTest