Source: https://fincobra.com/docs/checkout/agent-quickstart.html # Agent Quickstart Give this page to a coding agent when you want it to add FinCobra Checkout to an existing app. This guide describes the standard hosted checkout integration. The agent should add the frontend checkout flow, create invoices from the merchant server, redirect customers to the hosted `paymentUrl`, verify webhooks, and update orders from the current invoice state. Continuing without an agent? Start with [Authentication](./authentication.md). ## Agent prompt ```text Integrate FinCobra Checkout using the hosted payment page. Use the frontend for the customer checkout flow and the backend for all FinCobra API calls. Never expose the FinCobra API key or webhook signing secret to browser, mobile, or client-side code. Required behavior: 1. Add server-side configuration for FINCOBRA_CHECKOUT_API_KEY and FINCOBRA_CHECKOUT_WEBHOOK_SECRET. 2. Add a frontend checkout action that calls the merchant backend to start checkout for the current order. 3. In the merchant backend, create a FinCobra invoice with POST https://fincobra.com/api/checkout/invoices. 4. Store the returned invoice id and paymentUrl with the local order. 5. Return paymentUrl to the frontend and redirect the customer there. 6. Add frontend handling for the return page or order page so the customer can see pending, paid, expired, or voided checkout state from the merchant backend. 7. Add a webhook endpoint that verifies X-Checkout-Signature with HMAC-SHA256 over the raw JSON request body. 8. After a valid webhook, fetch GET https://fincobra.com/api/checkout/invoices/:id from the server and update the local order from the current invoice status. 9. Treat confirmed and paid_out_of_band as paid. Keep payment_detected pending unless this business accepts unconfirmed crypto payments. Use these docs as the source of truth: - Overview: https://fincobra.com/docs/checkout/ - TypeScript SDK: https://fincobra.com/docs/checkout/sdk.html - Invoices: https://fincobra.com/docs/checkout/invoices.html - Webhooks: https://fincobra.com/docs/checkout/webhooks.html - API Reference: https://fincobra.com/docs/checkout/api-reference.html ``` If the app is TypeScript or Node.js based, prefer the FinCobra SDK: ```bash npm install github:dexstandard/fincobra-sdk ``` Use `client.checkoutSessions.create(...)` or `client.invoices.create(...)` from the merchant backend, `client.payments.get(...)` for status checks, and `client.webhooks.verifySignature(...)` for webhook verification. ## Integration shape The integration has both frontend and backend work: | App layer | Responsibility | | --- | --- | | Frontend | Start checkout by calling the merchant backend, redirect to `paymentUrl`, and render local order status after the customer returns. | | Backend | Store secrets, create FinCobra invoices, store invoice IDs, verify webhooks, fetch current invoice state, and update local orders. | | FinCobra hosted page | Collect payment through the hosted checkout page and show live payment status to the customer. | Do not call FinCobra merchant endpoints directly from frontend code. The browser should only talk to the merchant backend and visit the hosted `paymentUrl`. ## Merchant setup Before code changes, the merchant should complete these dashboard steps: 1. Configure at least one payment method. 2. Set invoice timing, issuer name, and redirect URL defaults. 3. Create a Checkout API key. 4. Create a webhook signing secret. 5. Add the production webhook URL. Use these server-side environment variables: ```text FINCOBRA_CHECKOUT_API_KEY=fc_live_... FINCOBRA_CHECKOUT_WEBHOOK_SECRET=... FINCOBRA_CHECKOUT_BASE_URL=https://fincobra.com ``` ## Frontend checkout flow Add a checkout button or action that calls the merchant backend for the current order: ```javascript async function startCheckout(orderId) { const response = await fetch(`/api/orders/${orderId}/checkout`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, }); if (!response.ok) { throw new Error('Unable to start checkout'); } const checkout = await response.json(); window.location.assign(checkout.paymentUrl); } ``` The merchant backend response should contain the hosted `paymentUrl`, not the FinCobra API key. The hosted page redirects to the exact `redirectUrl` after `confirmed` or `paid_out_of_band`; no invoice params are appended. After the customer returns to the merchant app, show the local order state from the merchant backend. Use `confirmed` and `paid_out_of_band` as paid states. Show `payment_detected` as pending unless the business accepts unconfirmed crypto payments. ## Backend invoice creation Create invoices from the merchant server. Store the local order ID in `merchantReference` and `metadata` so webhook handling can reconcile the invoice. ```bash curl -X POST https://fincobra.com/api/checkout/invoices \ -H "X-Api-Key: fc_live_..." \ -H "Content-Type: application/json" \ -d '{ "amountUsd": 49.99, "productName": "Pro Plan - Monthly", "merchantReference": "order_123", "customerEmail": "buyer@example.com", "metadata": { "orderId": "order_123" } }' ``` Use the response like this: | Field | Required action | | --- | --- | | `id` | Store with the local order as the FinCobra invoice ID. | | `paymentUrl` | Redirect the customer to this URL. | | `status` | Store for admin display and reconciliation. | | `paymentSummary` | Store or read when handling partial payment and overpayment states. | For standard hosted checkout, do not build custom payment instructions from `paymentOptions[]`. Redirect to `paymentUrl`. ## Webhook handling FinCobra sends `POST` requests to the configured webhook URL with `X-Checkout-Signature`. The signature is an HMAC-SHA256 hex digest of the raw JSON request body using the webhook signing secret. ```javascript import crypto from 'node:crypto'; function verifyFinCobraSignature(rawBody, signature, secret) { if (typeof signature !== 'string') { return false; } if (!/^[0-9a-f]{64}$/i.test(signature)) { return false; } const expected = crypto .createHmac('sha256', secret) .update(rawBody) .digest('hex'); const signatureBuffer = Buffer.from(signature, 'hex'); const expectedBuffer = Buffer.from(expected, 'hex'); return ( signatureBuffer.length === expectedBuffer.length && crypto.timingSafeEqual(signatureBuffer, expectedBuffer) ); } ``` Use `payload.invoice.id` from the webhook body, then fetch the latest invoice before updating the order. After verifying the signature, fetch the latest invoice before changing order state: ```bash curl https://fincobra.com/api/checkout/invoices/a1b2c3d4-1111-4222-8333-abcdefabcdef \ -H "X-Api-Key: fc_live_..." ``` Webhook delivery is at-least-once. Make the local order update idempotent. ## Status mapping | FinCobra invoice status | Merchant order action | | --- | --- | | `awaiting_payment` | Keep pending. | | `partially_paid` | Keep pending and review `paymentSummary.remainingAmountUsd`. | | `payment_detected` | Keep pending unless the business accepts detected but unconfirmed payment. | | `confirmed` | Mark paid and fulfill if not already fulfilled. | | `paid_out_of_band` | Mark paid and store the payment note for reconciliation. | | `expired` | Mark checkout expired and let the customer start a new checkout. | | `voided` | Stop accepting payment for this invoice. | Use `paymentCoverage` and `paymentSummary` for partial payment and overpayment operations. Treat `confirmed` as the default fulfillment trigger. ## Implementation checklist - Keep the API key and webhook secret server-side. - Create invoices from the merchant backend, not the browser. - Save the FinCobra invoice ID on the local order. - Redirect customers to `paymentUrl`. - Verify webhook signatures using the raw request body. - Fetch current invoice state before updating orders. - Make webhook processing idempotent. - Add tests for invoice creation, webhook signature rejection, duplicate webhook delivery, and status-to-order mapping. --- Source: https://fincobra.com/docs/checkout/api-reference.html # API Reference Base URL: `https://fincobra.com` Use the FinCobra dashboard to configure Checkout, payment methods, webhook settings, and API keys. Use the API from your server to create invoices and read known invoice state. ## Authentication Merchant endpoints require an API key: ```http X-Api-Key: fc_live_... ``` API keys can create invoices and read known invoice data. They cannot list all invoices or change Checkout configuration. ## Merchant Endpoints | Method | Path | Description | | ------ | ------------------------------------------- | -------------------------- | | `POST` | `/api/checkout/invoices` | Create an invoice | | `GET` | `/api/checkout/invoices/:id` | Get invoice detail | | `GET` | `/api/checkout/invoices/:id/status` | Get invoice status | | `POST` | `/api/checkout/invoices/:id/void` | Void an unpaid invoice | | `POST` | `/api/checkout/invoices/:id/record-payment` | Record an external payment | ## Create Invoice ``` POST /api/checkout/invoices ``` Creates a checkout invoice using your configured payment methods. If Bitcoin address preparation is in progress and no other payment method is available, the endpoint returns `503` with an `error` message that asks you to retry shortly. Wait briefly, then retry the request with the same idempotency key. ### Request ```bash curl -X POST https://fincobra.com/api/checkout/invoices \ -H "X-Api-Key: fc_live_..." \ -H "Content-Type: application/json" \ -d '{ "amountUsd": 49.99, "productName": "Pro Plan - Monthly", "merchantReference": "order_123", "customerEmail": "buyer@example.com" }' ``` ### Request Body | Field | Type | Required | Description | | ------------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------ | | `amountUsd` | `number` | Yes | Invoice amount in USD. Minimum `0.01`. | | `paymentTiming` | `object` | No | Per-invoice timing override. Omit to use the dashboard default. | | `productName` | `string` | No | Product or service label. Max 200 chars. | | `issuedBy` | `string` | No | Invoice issuer name. Defaults to the dashboard setting. Max 120 chars. | | `billTo` | `string` | No | Customer or company name shown on the invoice. Max 120 chars. | | `redirectUrl` | `string` | No | Redirect URL after the payer-facing flow completes. | | `merchantReference` | `string` | No | Your order, cart, or internal reference. Max 200 chars. | | `customerId` | `string` | No | Your internal customer ID. Max 200 chars. | | `customerEmail` | `string` | No | Customer email for reconciliation. Max 320 chars. | | `metadata` | `object` | No | JSON object stored with the invoice. `metadata.notes`, when present, must be a string up to 140 chars. | ### Response Returns HTTP `201` with an [Invoice object](#invoice-object): ```json { "id": "a1b2c3d4-1111-4222-8333-abcdefabcdef", "paymentUrl": "https://fincobra.com/pay/a1b2c3d4-1111-4222-8333-abcdefabcdef", "amountUsd": 49.99, "status": "awaiting_payment", "paymentCoverage": "no_payment", "receivedAmountUsd": 0, "confirmedAmountUsd": 0, "confirmations": 0, "paymentTiming": { "mode": "immediate", "expiresAfterMinutes": 20, "payableUntilAt": "2026-04-29T10:20:00.000Z" }, "paymentDetectedAt": null, "confirmedAt": null, "createdAt": "2026-04-29T10:00:00.000Z", "productName": "Pro Plan - Monthly", "issuedBy": "Acme, Inc.", "billTo": null, "redirectUrl": "https://example.com/thanks", "lastTransactionHash": null, "merchantReference": "order_123", "customerId": null, "customerEmail": "buyer@example.com", "metadata": null, "paymentSummary": { "paymentCoverage": "no_payment", "receivedAmountUsd": 0, "confirmedAmountUsd": 0, "remainingAmountUsd": 49.99, "overpaymentAmountUsd": 0 }, "paymentOptions": [ { "id": "payment-option-1", "railType": "wallet_contract", "assetCode": "USDT", "network": "ethereum", "tokenContract": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "quotedAmount": 49.99, "quoteRate": 1, "destinationAddress": null, "status": "active", "isDefault": true, "paymentUri": null, "qrCode": null, "optionPayload": { "railType": "wallet_contract", "chainId": 1, "contractAddress": "0x...", "tokenContract": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "recipient": "0x1111111111111111111111111111111111111111", "decimals": 6, "requiredConfirmations": 14, "paymentAmountAtomic": "49990000" } } ], "defaultPaymentOptionId": "payment-option-default", "lastPaymentOptionId": "payment-option-1", "lastPaymentObservedAt": null } ``` Use `paymentUrl` for hosted checkout. Use `paymentOptions[]` only when building custom payment UI. ## Get Invoice Detail ``` GET /api/checkout/invoices/:id ``` Returns a known invoice by `id`. ```bash curl https://fincobra.com/api/checkout/invoices/a1b2c3d4-1111-4222-8333-abcdefabcdef \ -H "X-Api-Key: fc_live_..." ``` Returns an [Invoice object](#invoice-object), including `payments[]` when payment events have been observed: ```json { "id": "a1b2c3d4-1111-4222-8333-abcdefabcdef", "paymentUrl": "https://fincobra.com/pay/a1b2c3d4-1111-4222-8333-abcdefabcdef", "amountUsd": 49.99, "status": "payment_detected", "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "confirmations": 1, "paymentTiming": { "mode": "immediate", "expiresAfterMinutes": 20, "payableUntilAt": "2026-04-29T10:20:00.000Z" }, "paymentDetectedAt": "2026-04-29T10:05:00.000Z", "confirmedAt": null, "createdAt": "2026-04-29T10:00:00.000Z", "productName": "Pro Plan - Monthly", "issuedBy": "Acme, Inc.", "billTo": null, "redirectUrl": "https://example.com/thanks", "lastTransactionHash": "0xabc123...", "merchantReference": "order_123", "customerId": null, "customerEmail": "buyer@example.com", "metadata": { "orderId": "order_123" }, "paymentSummary": { "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "remainingAmountUsd": 0, "overpaymentAmountUsd": 0 }, "paymentOptions": [ { "id": "payment-option-1", "railType": "wallet_contract", "assetCode": "USDT", "network": "ethereum", "tokenContract": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "quotedAmount": 49.99, "quoteRate": 1, "destinationAddress": null, "status": "active", "isDefault": true, "paymentUri": null, "qrCode": null, "optionPayload": { "railType": "wallet_contract", "chainId": 1, "contractAddress": "0x...", "tokenContract": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "recipient": "0x1111111111111111111111111111111111111111", "decimals": 6, "requiredConfirmations": 14, "paymentAmountAtomic": "49990000" } } ], "defaultPaymentOptionId": "payment-option-default", "lastPaymentOptionId": "payment-option-1", "payments": [ { "id": "payment-1", "paymentOptionId": "payment-option-1", "assetCode": "USDT", "network": "ethereum", "destinationAddress": null, "transactionHash": "0xabc123...", "amountReceived": 49.99, "observedTotalAmount": 49.99, "confirmations": 1, "observedAt": "2026-04-29T10:05:00.000Z", "confirmedAt": null, "invalidatedAt": null, "invalidationReason": null } ], "lastPaymentObservedAt": "2026-04-29T10:05:00.000Z" } ``` ## Get Invoice Status ``` GET /api/checkout/invoices/:id/status ``` Returns a polling-friendly status payload for a known invoice: ```bash curl https://fincobra.com/api/checkout/invoices/a1b2c3d4-1111-4222-8333-abcdefabcdef/status \ -H "X-Api-Key: fc_live_..." ``` ```json { "status": "payment_detected", "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "confirmations": 1, "paymentDetectedAt": "2026-04-29T10:05:00.000Z", "confirmedAt": null, "paidOutOfBandAt": null, "paidOutOfBandNote": null, "paymentTiming": { "mode": "immediate", "expiresAfterMinutes": 20, "payableUntilAt": "2026-04-29T10:20:00.000Z" }, "paymentSummary": { "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "remainingAmountUsd": 0, "overpaymentAmountUsd": 0 }, "paymentOptions": [ { "id": "payment-option-1", "railType": "wallet_contract", "assetCode": "USDT", "network": "ethereum", "tokenContract": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "quotedAmount": 49.99, "quoteRate": 1, "destinationAddress": null, "status": "active", "isDefault": true, "paymentUri": null, "qrCode": null, "optionPayload": { "railType": "wallet_contract", "chainId": 1, "contractAddress": "0x...", "tokenContract": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "recipient": "0x1111111111111111111111111111111111111111", "decimals": 6, "requiredConfirmations": 14, "paymentAmountAtomic": "49990000" } } ], "defaultPaymentOptionId": "payment-option-default", "lastPaymentOptionId": "payment-option-1", "payments": [ { "id": "payment-1", "paymentOptionId": "payment-option-1", "assetCode": "USDT", "network": "ethereum", "destinationAddress": null, "transactionHash": "0xabc123...", "amountReceived": 49.99, "observedTotalAmount": 49.99, "confirmations": 1, "observedAt": "2026-04-29T10:05:00.000Z", "confirmedAt": null, "invalidatedAt": null, "invalidationReason": null } ], "lastPaymentObservedAt": "2026-04-29T10:05:00.000Z" } ``` ## Void Invoice ``` POST /api/checkout/invoices/:id/void ``` Voids an invoice that is still `awaiting_payment`. ```bash curl -X POST https://fincobra.com/api/checkout/invoices/a1b2c3d4-1111-4222-8333-abcdefabcdef/void \ -H "X-Api-Key: fc_live_..." \ -H "Content-Type: application/json" \ -d '{}' ``` Returns the updated invoice object. Requests for invoices in any other status return HTTP `409`. ## Record Payment ``` POST /api/checkout/invoices/:id/record-payment ``` Records an external payment for an invoice in `awaiting_payment`, `partially_paid`, or `expired`. ```bash curl -X POST https://fincobra.com/api/checkout/invoices/a1b2c3d4-1111-4222-8333-abcdefabcdef/record-payment \ -H "X-Api-Key: fc_live_..." \ -H "Content-Type: application/json" \ -d '{ "note": "Paid by wire transfer" }' ``` ### Request Body | Field | Type | Required | Description | | ------ | -------- | -------- | ------------------------------------------------------------- | | `note` | `string` | Yes | Reconciliation note for the external payment. Max 2000 chars. | Returns the updated invoice object with `status: "paid_out_of_band"`, `paidOutOfBandAt`, and `paidOutOfBandNote`. ## Public Payment Page API These unauthenticated endpoints power the hosted payment page and can be used for custom payment pages. Do not use them for private merchant metadata or server-side reconciliation. | Method | Path | Description | | ------ | ----------------------------------------------------- | --------------------------------------------- | | `GET` | `/api/checkout/pay/:id` | Get customer-facing invoice data | | `GET` | `/api/checkout/pay/:id/status` | Poll customer-facing invoice status | | `POST` | `/api/checkout/pay/:id/prepare-solana-wallet-payment` | Prepare an unsigned Solana wallet transaction | ### Get Public Invoice ``` GET /api/checkout/pay/:id ``` Returns customer-facing invoice data, payment instructions, and public payment status. It does not require an API key. Public responses intentionally exclude merchant-only fields such as `merchantReference`, `customerId`, `customerEmail`, and `metadata`. Customer-facing fields such as `issuedBy`, `billTo`, `productName`, `paymentTiming`, and `redirectUrl` may be included. Merchant reconciliation notes for out-of-band payments are not returned by public endpoints. ```json { "id": "a1b2c3d4-1111-4222-8333-abcdefabcdef", "paymentUrl": "https://fincobra.com/pay/a1b2c3d4-1111-4222-8333-abcdefabcdef", "amountUsd": 49.99, "status": "awaiting_payment", "paymentCoverage": "no_payment", "receivedAmountUsd": 0, "confirmedAmountUsd": 0, "confirmations": 0, "paymentTiming": { "mode": "immediate", "expiresAfterMinutes": 20, "payableUntilAt": "2026-04-29T10:20:00.000Z" }, "paymentDetectedAt": null, "confirmedAt": null, "createdAt": "2026-04-29T10:00:00.000Z", "productName": "Pro Plan - Monthly", "issuedBy": "Acme, Inc.", "billTo": null, "redirectUrl": "https://example.com/thanks", "lastTransactionHash": null, "paymentSummary": { "paymentCoverage": "no_payment", "receivedAmountUsd": 0, "confirmedAmountUsd": 0, "remainingAmountUsd": 49.99, "overpaymentAmountUsd": 0 }, "paymentOptions": [ { "id": "payment-option-1", "railType": "address_transfer", "assetCode": "BTC", "network": "bitcoin", "tokenContract": null, "quotedAmount": 0.00073, "quoteRate": 68479.45, "destinationAddress": "bc1qexample...", "status": "active", "isDefault": true, "paymentUri": "bitcoin:bc1qexample...?amount=0.00073", "qrCode": "...", "optionPayload": { "railType": "address_transfer", "requiredConfirmations": 1 } } ], "defaultPaymentOptionId": "payment-option-default", "lastPaymentOptionId": "payment-option-1", "payments": [], "lastPaymentObservedAt": null } ``` ### Get Public Invoice Status ``` GET /api/checkout/pay/:id/status ``` Returns the current customer-facing status for an invoice. Use this endpoint when building a custom payment page that polls for payment progress. ```json { "status": "payment_detected", "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "confirmations": 1, "paymentDetectedAt": "2026-04-29T10:05:00.000Z", "confirmedAt": null, "paidOutOfBandAt": null, "paidOutOfBandNote": null, "paymentTiming": { "mode": "immediate", "expiresAfterMinutes": 20, "payableUntilAt": "2026-04-29T10:20:00.000Z" }, "paymentSummary": { "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "remainingAmountUsd": 0, "overpaymentAmountUsd": 0 }, "paymentOptions": [ { "id": "payment-option-1", "railType": "address_transfer", "assetCode": "BTC", "network": "bitcoin", "tokenContract": null, "quotedAmount": 0.00073, "quoteRate": 68479.45, "destinationAddress": "bc1qexample...", "status": "active", "isDefault": true, "paymentUri": "bitcoin:bc1qexample...?amount=0.00073", "qrCode": null, "optionPayload": { "railType": "address_transfer", "requiredConfirmations": 1 } } ], "defaultPaymentOptionId": "payment-option-default", "lastPaymentOptionId": "payment-option-1", "payments": [ { "id": "payment-1", "paymentOptionId": "payment-option-1", "assetCode": "BTC", "network": "bitcoin", "destinationAddress": "bc1qexample...", "transactionHash": "btc_tx_hash", "amountReceived": 0.00073, "observedTotalAmount": 0.00073, "confirmations": 1, "observedAt": "2026-04-29T10:05:00.000Z", "confirmedAt": null, "invalidatedAt": null, "invalidationReason": null } ], "lastPaymentObservedAt": "2026-04-29T10:05:00.000Z" } ``` ### Prepare Solana Wallet Payment ``` POST /api/checkout/pay/:id/prepare-solana-wallet-payment ``` Prepares an unsigned Solana mainnet transaction for the selected invoice payment option. The transaction includes the exact remaining token amount, merchant associated token account creation when needed, invoice reference, and memo. Submit the returned transaction to the connected Solana browser wallet for signing. ```json { "paymentOptionId": "11111111-1111-4111-8111-111111111111", "payerPublicKey": "PayerSolanaWallet111111111111111111111111111" } ``` The response contains a base64-encoded unsigned transaction and its preparation metadata: ```json { "transaction": "AQAAAAAAAA...", "blockhash": "RecentSolanaBlockhash11111111111111111111111", "lastValidBlockHeight": 312345678, "paymentOptionId": "11111111-1111-4111-8111-111111111111", "amountAtomic": "49990000" } ``` The endpoint returns `409` when the invoice or selected option is not payable, `429` when the public preparation limit is exceeded, and `503` when Solana transaction preparation is temporarily unavailable. ## Objects ### Invoice Object | Field | Type | Description | | ------------------------ | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | `id` | `string` | Invoice UUID. Store this with your order. | | `paymentUrl` | `string` | Hosted payment page URL. | | `amountUsd` | `number` | Invoice amount in USD. | | `status` | `string` | Main invoice state. See [Status values](#status-values). | | `paymentCoverage` | `string` | Whether payment is missing, partial, exact, or overpaid. | | `receivedAmountUsd` | `number` | USD amount received across all valid payment options. | | `confirmedAmountUsd` | `number` | USD amount that has reached the confirmation threshold. | | `confirmations` | `number` | Confirmations observed for the invoice-counted payment. | | `productName` | `string or null` | Product or service label. | | `issuedBy` | `string or null` | Invoice issuer name shown on the hosted page. | | `billTo` | `string or null` | Customer or company name shown on the hosted page. | | `redirectUrl` | `string or null` | URL used after the payer-facing flow completes. | | `lastTransactionHash` | `string or null` | Latest transaction hash observed for invoice-counted payment evidence. | | `merchantReference` | `string or null` | Your order, cart, or internal reference. | | `customerId` | `string or null` | Your internal customer ID. | | `customerEmail` | `string or null` | Customer email for reconciliation. | | `metadata` | `object or null` | Merchant metadata stored with the invoice. | | `paymentSummary` | `object` | Amount received, remaining, and overpaid. | | `paymentTiming` | `object` | Immediate or due-date timing. | | `paymentOptions` | `array` | Payment instructions available for this invoice. | | `defaultPaymentOptionId` | `string or null` | Initial hosted page payment option for the invoice. | | `lastPaymentOptionId` | `string or null` | Latest observed payment option for the invoice. | | `payments` | `array` | Observed payment events. Present on detail/status responses. | | `paymentDetectedAt` | `string or null` | ISO timestamp when sufficient payment was first detected. | | `confirmedAt` | `string or null` | ISO timestamp when sufficient payment reached the confirmation threshold. | | `paidOutOfBandAt` | `string or null` | ISO timestamp when an external payment was recorded. | | `paidOutOfBandNote` | `string or null` | Reconciliation note for an external payment. | | `exceptionType` | `late_payment`, `partial_payment`, or `null` | Exception type when merchant review is needed. | | `exceptionStatus` | `open`, `closed`, or `null` | Current exception state. | | `exceptionAction` | `accept_late_payment`, `mark_paid_out_of_band`, `close_unpaid`, or `null` | Merchant action used when the exception was closed. | | `exceptionNote` | `string or null` | Merchant note recorded when the exception was closed. | | `exceptionClosedAt` | `string or null` | ISO timestamp when the exception was closed. | | `createdAt` | `string` | ISO timestamp when the invoice was created. | | `lastPaymentObservedAt` | `string or null` | ISO timestamp for the latest observed payment event. | ### Payment Summary Object | Field | Type | Description | | ---------------------- | -------- | -------------------------------------------------------- | | `paymentCoverage` | `string` | Whether payment is missing, partial, exact, or overpaid. | | `receivedAmountUsd` | `number` | USD amount received across all valid payment options. | | `remainingAmountUsd` | `number` | USD amount still due. | | `overpaymentAmountUsd` | `number` | USD amount overpaid. | ### Payment Timing Object Immediate timing: ```json { "mode": "immediate", "expiresAfterMinutes": 20, "payableUntilAt": "2026-04-29T10:20:00.000Z" } ``` Due-date timing: ```json { "mode": "due_date", "dueAfterDays": 7, "gracePeriodDays": 14, "dueAt": "2026-05-06T10:00:00.000Z", "payableUntilAt": "2026-05-20T10:00:00.000Z" } ``` Due-date invoices require an enabled USDT or USDC payment method. Bitcoin payment methods are only used for immediate invoices. ### Payment Option Object | Field | Type | Description | | -------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `string` | Payment option ID. | | `railType` | `wallet_contract` or `address_transfer` | `wallet_contract` for EVM hosted wallet contracts. `address_transfer` for Bitcoin, EVM address-transfer, and Solana direct token-transfer options. | | `assetCode` | `string` | Asset shown to the payer, such as `USDT`, `USDC`, or `BTC`. | | `network` | `string` | Network shown to the payer, such as `bitcoin`, `ethereum`, `solana`, `arbitrum`, or `base`. | | `tokenContract` | `string or null` | Token contract address for token-based options. | | `quotedAmount` | `number` | Amount due in the option asset. | | `quoteRate` | `number` | Rate used to quote the asset amount. | | `destinationAddress` | `string or null` | Destination address for address-transfer payment options. | | `paymentUri` | `string or null` | Payment URI for address-transfer options. Solana options return a `solana:` transfer URI for custom wallet tooling. | | `qrCode` | `string or null` | SVG QR code for address-transfer options when requested. The hosted Solana payer flow uses wallet connection and approval instead. | | `status` | `string` | Payment option status. | | `isDefault` | `boolean` | Whether this is the default option on the hosted page. | | `optionPayload` | `object or null` | Executable payment data for custom wallet UI. Use `optionPayload.methodType` to branch between EVM and Solana payloads. | For standard hosted checkout, redirect to `paymentUrl` and ignore `paymentOptions[]`. Solana wallet options use `railType: "address_transfer"` and `network: "solana"` because the connected wallet signs a direct SPL-token transfer. The hosted payment page asks the payer to connect a Solana wallet in the current browser, then approve the quoted USDT or USDC transfer. The transfer settles directly to the configured merchant Solana recipient. Custom payment UIs should request the unsigned transaction from the prepare endpoint and submit it to the connected wallet for signing. Example Solana wallet option: ```json { "id": "11111111-1111-4111-8111-111111111111", "railType": "address_transfer", "assetCode": "USDC", "network": "solana", "tokenContract": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "quotedAmount": 49.99, "quoteRate": 1, "destinationAddress": "MerchantSolanaWallet1111111111111111111111111", "status": "active", "isDefault": true, "paymentUri": "solana:MerchantSolanaWallet1111111111111111111111111?amount=49.99&spl-token=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&reference=InvoiceReference1111111111111111111111111111&label=FinCobra+Checkout&memo=fincobra%3Aa1b2c3d4-1111-4222-8333-abcdefabcdef%3A11111111-1111-4111-8111-111111111111", "qrCode": "...", "optionPayload": { "railType": "address_transfer", "methodType": "solana", "paymentUri": "solana:MerchantSolanaWallet1111111111111111111111111?amount=49.99&spl-token=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&reference=InvoiceReference1111111111111111111111111111&label=FinCobra+Checkout&memo=fincobra%3Aa1b2c3d4-1111-4222-8333-abcdefabcdef%3A11111111-1111-4111-8111-111111111111", "recipientAddress": "MerchantSolanaWallet1111111111111111111111111", "reference": "InvoiceReference1111111111111111111111111111", "mintAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "decimals": 6, "amountAtomic": "49990000", "memo": "fincobra:a1b2c3d4-1111-4222-8333-abcdefabcdef:11111111-1111-4111-8111-111111111111", "requiredConfirmations": 32 } } ``` ### Payment Object | Field | Type | Description | | --------------------- | ---------------- | ------------------------------------------------------------------------------------------------ | | `id` | `string` | Observed payment ID. | | `paymentOptionId` | `string` | Payment option matched by the observation. | | `assetCode` | `string` | Observed asset. | | `network` | `string` | Observed network. | | `destinationAddress` | `string or null` | Observed destination address when applicable. | | `transactionHash` | `string or null` | On-chain transaction hash when available. | | `amountReceived` | `number` | Amount counted from this observation. | | `observedTotalAmount` | `number` | Total amount observed for the matched payment evidence. | | `confirmations` | `number` | Confirmations observed for this payment. | | `observedAt` | `string` | ISO timestamp when payment was observed. | | `confirmedAt` | `string or null` | ISO timestamp when this payment was confirmed. | | `invalidatedAt` | `string or null` | ISO timestamp when previously observed evidence was invalidated, if applicable. | | `invalidationReason` | `string or null` | Reason a previously observed payment no longer counts toward the invoice payment, if applicable. | ## Status Values | Field | Values | | ----------------- | -------------------------------------------------------------------------------------------------------------- | | `status` | `awaiting_payment`, `partially_paid`, `payment_detected`, `confirmed`, `paid_out_of_band`, `expired`, `voided` | | `paymentCoverage` | `no_payment`, `partial_payment`, `exact_payment`, `overpayment` | Use `status` for fulfillment decisions. `paymentCoverage` and `paymentSummary` explain underpayment and overpayment. A payment first observed at or before `paymentTiming.payableUntilAt` is on time; a payment first observed after that timestamp is late. Overpayment during the payable window does not open an exception; the invoice remains in its payment lifecycle state and exposes the excess amount in `paymentSummary.overpaymentAmountUsd`. A late payment never confirms an `expired` or `voided` invoice automatically. Late full payment opens a `late_payment` exception, and the invoice becomes `confirmed` only after the merchant closes it with `accept_late_payment`. `confirmedAmountUsd` shows how much USD has reached the confirmation threshold. Use `payments[]` for payment-level details such as transaction hash, observed asset amount, USD credit, confirmations, and `confirmedAt`. ## Rate Limits API-key-authenticated checkout requests are limited to 100 requests per minute from the same client. This limit applies to: | Method | Path | | ------ | ------------------------------------------- | | `POST` | `/api/checkout/invoices` | | `GET` | `/api/checkout/invoices/:id` | | `GET` | `/api/checkout/invoices/:id/status` | | `POST` | `/api/checkout/invoices/:id/void` | | `POST` | `/api/checkout/invoices/:id/record-payment` | Requests over the limit return HTTP `429`. Use the retry time in the error response before sending more requests. ## Error Format Errors use a JSON object with an `error` message: ```json { "error": "Human-readable error message" } ``` Validation errors name the first invalid field and the relevant constraint: ```json { "error": "productName must be at most 200 characters" } ``` ## HTTP Status Codes | Code | Meaning | | ----- | ------------------------------------------------------------------------------------------------------------------------- | | `200` | Success | | `201` | Invoice created | | `400` | Invalid request body, query, or invoice ID. The error message names the invalid field when validation fails. | | `401` | Missing or invalid API key | | `403` | Billing blocked invoice creation, or the API key cannot access the requested route | | `404` | Checkout config or invoice not found | | `409` | Incompatible invoice timing or unavailable payment method | | `429` | Rate limit exceeded. API-key-authenticated checkout requests are limited to 100 requests per minute from the same client. | | `502` | Payment-method or wallet backend unavailable | --- Source: https://fincobra.com/docs/checkout/authentication.html # Authentication Merchant integrations authenticate with an API key in the `X-Api-Key` header. ```bash curl -X POST https://fincobra.com/api/checkout/invoices \ -H "X-Api-Key: fc_live_abc123..." \ -H "Content-Type: application/json" \ -d '{ "amountUsd": 49.99, "merchantReference": "order_123" }' ``` ## Generate an API key Create and manage API keys from the FinCobra dashboard. 1. Open the Checkout dashboard. 2. Go to Checkout settings. 3. Generate an API key. 4. Copy the raw key immediately. 5. Store it in your server-side environment or secret manager. API keys are prefixed with `fc_live_`. The raw key is shown once at creation time and cannot be retrieved later. FinCobra stores only a SHA-256 hash of the key. ::: warning Do not put API keys in browser code, mobile apps, public repositories, or client-side environment variables. Use them only from your server. ::: ## Available API API keys are integration credentials. They can create invoices and read known invoice data: | Method | Path | Description | | --- | --- | --- | | `POST` | `/api/checkout/invoices` | Create an invoice | | `GET` | `/api/checkout/invoices/:id` | Get invoice detail | | `GET` | `/api/checkout/invoices/:id/status` | Get invoice status | API keys cannot list all invoices or change Checkout configuration. Store the invoice `id` returned by `POST /api/checkout/invoices` with your order, then use that known `id` for later lookups. Checkout configuration, payment methods, webhook settings, exports, exception review, and key rotation are handled in the FinCobra dashboard. ## Rate Limits API-key-authenticated checkout requests are limited to 100 requests per minute from the same client. This applies to invoice create, invoice detail, and invoice status calls made with the `X-Api-Key` header. If you exceed the limit, the API returns HTTP `429` with a human-readable error message. Retry after the time shown in the error response. ## Errors Requests without an API key or dashboard session return HTTP `401`: ```json { "error": "Missing API key or session" } ``` Invalid API keys also return HTTP `401`: ```json { "error": "Invalid API key" } ``` API keys require an enabled account. A disabled account receives HTTP `403` with `{ "error": "Forbidden" }`. This also applies to WooCommerce installation keys. Enabling the account restores access through keys that have not been revoked. If account status cannot be verified, authenticated requests return HTTP `503`. Retry after the service recovers. ## Account security Private Checkout list routes use the authenticated account. Do not add a `userId` or `user_id` query field to select another account. A confirmed cross-account probe can disable the calling account and revoke its sessions and MCP access tokens. Admin account management uses the Console routes. --- Source: https://fincobra.com/docs/checkout/billing.html # Billing FinCobra Checkout billing is automatic. There are no manual plan switches. Every merchant starts on Free Start. The first $1,000 of paid invoice volume is free. After that one-time free volume is used, the account automatically moves to Pay as You Go. ## Pay as You Go Pay as You Go is a flat 0.5% of paid invoice volume after Free Start. It is billed monthly in arrears from paid invoice volume in the billing cycle. For example, after Free Start is used, $12,000 of paid invoice volume in a cycle creates $60.00 in fees. ## What Counts Billing uses the USD invoice amount locked at invoice creation. Volume counts when the invoice payment is confirmed or when payment is recorded out of band. Invoices that are never paid do not count. Overpayment above the invoice total does not count toward billed volume. ## Billing Invoices Billing invoices are created after each Pay as You Go cycle ends. They are due 7 days after issue. If a billing invoice remains unpaid after the due date, the account can become past due and then blocked until payment is confirmed. --- Source: https://fincobra.com/docs/checkout/configuration.html # Configuration Before creating invoices, configure Checkout in the FinCobra dashboard. The dashboard controls payment methods, invoice defaults, webhook delivery, and API keys. Merchant integrations use API keys only for invoice creation and invoice lookup. ## Setup Checklist 1. Add at least one payment method. 2. Set default invoice timing. 3. Add your invoice issuer name. 4. Configure a redirect URL if customers should return to your site after payment. 5. Configure a webhook URL if your server needs payment notifications. 6. Generate and copy an API key. 7. Optional: [install FinCobra Checkout on iPhone or Android](./mobile-app.md) for quick access from your phone. ## Payment Methods Checkout supports these method types: | Method type | Use for | | ----------- | ---------------------------------------------------------------------------------------------------- | | `bitcoin` | Bitcoin address-transfer checkout | | `evm` | Ethereum, Arbitrum One, and Base stablecoin checkout with wallet-contract and address-transfer rails | | `solana` | Solana USDT and USDC wallet checkout | Invoices are built from the enabled payment methods. Each created invoice returns `paymentOptions[]`, which describes the available rails for that invoice. EVM payment methods require at least one enabled rail. Supported networks and assets: | Network | Assets | Dashboard setting | | ---------------- | ---------- | ----------------------- | | Bitcoin | BTC | Bitcoin xpub | | Ethereum mainnet | USDT, USDC | Ethereum payout wallet | | Solana | USDT, USDC | Solana recipient wallet | | Arbitrum One | USDC | Arbitrum payout wallet | | Base | USDC | Base payout wallet | Solana wallet payments and Bitcoin address-transfer payments settle directly to the recipient wallet configured for the payment method. EVM wallet-contract payments and address-transfer withdrawals settle to the payout wallet configured for that specific EVM network. FinCobra records invoice volume and commission internally for billing where funds are sent directly to the merchant wallet. Due-date invoices require an enabled USDT or USDC payment method. Bitcoin payment methods are only used for immediate invoices. A Bitcoin extended public key can belong to only one Checkout configuration. Attempts to use a key that is already configured elsewhere return `409`. Each Checkout configuration supports one payment method for each method type, asset, and network. Adding the same combination again returns `409`. Update the existing payment method instead. ## Finding Your Bitcoin xpub Export the extended public key for the Bitcoin account that should receive checkout payments. Wallets commonly expose it in the account details, export, or advanced settings as an `xpub`, `ypub`, or `zpub`. FinCobra only needs the extended **public** key; never paste a seed phrase or private key. For hardware wallets, open the Bitcoin account in the wallet's companion app and look for an account-public-key or extended-public-key export. If the wallet provides several formats, use the key for the address type you already receive to: `xpub` for legacy addresses, `ypub` for nested SegWit, or `zpub` for native SegWit. ## Invoice Defaults | Setting | Description | | ------------------------- | -------------------------------------------------------------------------------------- | | Default payment timing | Timing used when an API request omits `paymentTiming`. | | Bitcoin post-expiry grace | How long to keep watching BTC invoice addresses after the final payable deadline. | | Invoice issuer name | Default `issuedBy` value and organization name shown on hosted invoices. | | Customer emails | Optional invoice-created and payment-detected email notifications. | | Redirect URL | Default URL used after payment confirmation. Can be overridden per invoice. | | Invoice form fields | Dashboard-only setting for optional fields shown in the dashboard create-invoice form. | ## Checkout Appearance The General tab can set a Checkout avatar and a single background image for hosted invoice pages. The background spans the full desktop and mobile payment page, with separate contrast overlays keeping the invoice summary and payment controls readable. Avatar and background uploads accept PNG, JPEG, and WebP images up to 40 megapixels. The file content must match its declared image type. Transparent images are supported. FinCobra resizes and stores them as compressed WebP files. Checkout appearance is separate from donation-page appearance. ### Immediate Timing Use immediate timing for standard checkout flows with a countdown timer: ```json { "paymentTiming": { "mode": "immediate", "expiresAfterMinutes": 20 } } ``` `expiresAfterMinutes` must be between `1` and `1440`. ### Due-Date Timing Use due-date timing when an invoice should remain payable through a due date and grace period: ```json { "paymentTiming": { "mode": "due_date", "dueAfterDays": 7, "gracePeriodDays": 14 } } ``` `dueAfterDays` must be between `1` and `60`. `gracePeriodDays` must be between `0` and `90`. Due-date invoices do not show a live expiration countdown. They remain payable until the final payable deadline (`dueAt + gracePeriodDays`) and then expire. ## Webhooks Set a webhook URL in the dashboard to receive invoice events. Use the API settings event switches to choose which webhook events are delivered. Create a webhook signing secret before relying on webhook delivery. FinCobra signs webhook payloads with your webhook signing secret. The webhook secret is shown when it is created or rotated. Copy it immediately and store it in your server-side secret manager. See [Webhooks](./webhooks.md) for payloads and signature verification. ## API Keys Generate API keys from the dashboard. The raw key is shown once and cannot be retrieved later. Use API keys only from your server: ```http X-Api-Key: fc_live_... ``` API keys can create invoices and read invoice-specific data. Dashboard configuration, payment-method management, webhook settings, exports, and exception closing are managed in the dashboard. ## Account security checks Checkout checks payment recipients and webhook destinations against the security rules managed by administrators in Console when you save settings. A match prevents the settings from being saved, disables the account across FinCobra services, and revokes its sessions. API keys cannot authorize requests while their owner is disabled. Contact support if you think an account was disabled in error. ## Account email delivery An administrator can unsubscribe a user from non-critical emails in the Console customer details page. This setting blocks billing messages, lifecycle messages, and checkout messages sent to that user's email address. Password reset and email verification messages remain enabled. Disabled accounts receive no emails, including password reset and email verification messages. Checkout does not send invoice messages for a disabled merchant account. A merchant's email subscription setting does not change delivery to a different buyer email address. --- Source: https://fincobra.com/docs/checkout/donate-button.html # Donation pages Donation pages give supporters focused places to contribute with your existing receiving wallets. You can publish up to three pages with separate public identities, visuals, amount suggestions, links, and embed buttons. Donations from every page appear in the Donations app. ## Setup Open **Donations → Settings**. Select an existing page from the page tabs, or choose **Create page** while fewer than three pages exist. Then open **Donation page**. Configure: - Public name, tagline, and description - Avatar image - Page background - Public slug for `/donate/:slug` - Optional description - Button label - Amount mode - Three suggested USD amounts - Enabled state All donation pages use the wallets configured under **Donations → Settings → Receiving**. Changing Receiving updates the available currencies for every page without changing their public content. Avatar and background uploads accept PNG, JPEG, and WebP images up to 40 megapixels. The file content must match its declared image type. Transparent images are supported. Each page keeps its own active visuals. Deleting a page disables its public link and frees one of the three page slots. Previously received donations remain in your history. ## Amount modes | Mode | Donor experience | | ---------------------- | ----------------------------------------------------------- | | Suggestions and custom | Three quick amount buttons plus a custom amount input | | Custom only | A custom amount input without preset amount buttons | | Suggestions only | Donor must choose one of the three configured amount values | ## Public page The public page is: ``` https://fincobra.com/donate/:slug ``` When a supporter submits an amount, FinCobra starts the payment flow on the donation page and records the result in Donations. ## Embed snippet Select the page under **Donations → Settings**, then open **Embed** to copy that page's snippet. The settings page generates a snippet for your website: ```html ``` Each snippet links to the selected public donation page. The hosted payment page should open directly in the browser and is not designed for iframe embedding. --- Source: https://fincobra.com/docs/checkout/ # Checkout API FinCobra Checkout lets you accept payments programmatically. Funds go directly to your wallet - no custodial intermediary. ## Quickstart Use this path for a standard server-side checkout integration: 1. **Configure Checkout** in the dashboard. Add at least one payment method, set the default invoice timing, set your issuer name, and add a webhook URL if your server needs payment updates. 2. **Create an API key** in Checkout settings. Copy it once and store it in your server-side environment or secret manager. 3. **Create an invoice** from your server: ```bash curl -X POST https://fincobra.com/api/checkout/invoices \ -H "X-Api-Key: fc_live_..." \ -H "Content-Type: application/json" \ -d '{ "amountUsd": 49.99, "productName": "Pro Plan - Monthly", "merchantReference": "order_123", "customerEmail": "buyer@example.com" }' ``` 4. **Store the returned `id` and `paymentUrl`** with your order. 5. **Send the customer to `paymentUrl`**. The hosted payment page handles wallet connection, Bitcoin QR codes, address-transfer instructions, and live status updates. 6. **Verify webhooks** with your signing secret. When a webhook arrives, fetch the latest invoice status and update your order from the current invoice state. For most integrations, you only need to create invoices, redirect customers to `paymentUrl`, and listen for webhooks. Prefer TypeScript? Use the [FinCobra SDK](./sdk.md) for `client.checkoutSessions.create(...)`, `client.invoices.create(...)`, `client.payments.get(...)`, and webhook signature verification. ## Coding agent setup If you are using a coding agent to add FinCobra Checkout to an app, give it the [Agent Quickstart](./agent-quickstart.md). It includes the exact hosted-checkout implementation scope, webhook verification requirements, status mapping, and tests to add. ## Core Concepts | Concept | What to use it for | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | `paymentUrl` | Hosted payment page URL. Redirect customers here unless you are building a custom payment page. | | `status` | Main invoice state for fulfillment. Treat `confirmed` as paid. Treat `payment_detected` as received but not fully confirmed. | | Webhooks | Server-to-server notifications for invoice state changes. Always verify the signature before processing. | | `merchantReference` | Your order, cart, or internal reference. Store it so webhook payloads can be matched to your system. | | `paymentSummary` | Amount received, remaining, and overpaid. Use this when handling partial or overpaid invoices. | | `paymentOptions[]` | Available payment instructions for the invoice. Most integrations do not need this unless building custom payment UI. | | Billing | First $1,000 of paid invoice volume is free, then Pay as You Go starts automatically. | ## Payment methods The checkout model is payment-method based: - `evm` for USDT and USDC stablecoin checkout on Ethereum, Arbitrum One, and Base - `solana` for Solana wallet checkout with USDT and USDC direct wallet settlement - `bitcoin` for Bitcoin address generation Invoices are built from the payment methods enabled in the dashboard. You do not need to choose a method when creating a standard hosted-page invoice. ## Supported Chains | Network | Assets | Settlement | | ---------------- | ---------- | -------------------------------------------------------------------- | | Bitcoin | BTC | Unique invoice addresses derived from your Bitcoin xpub | | Ethereum mainnet | USDT, USDC | Wallet-contract payments and deterministic address-transfer invoices | | Solana | USDT, USDC | Customers connect a Solana wallet and approve payment to your wallet | | Arbitrum One | USDC | Wallet-contract payments and deterministic address-transfer invoices | | Base | USDC | Wallet-contract payments and deterministic address-transfer invoices | ## Base URL All API endpoints are relative to: ``` https://fincobra.com ``` ## Next steps - [Authentication](./authentication.md) - authenticate server-side API requests - [Agent Quickstart](./agent-quickstart.md) - give this implementation brief to a coding agent - [TypeScript SDK](./sdk.md) - install the SDK and copy Next.js, Express, and webhook examples - [Configuration](./configuration.md) - configure payment methods, invoice defaults, webhooks, and API keys - [Billing](./billing.md) - understand Free Start and flat Pay as You Go pricing - [WooCommerce](./woocommerce.md) - download the plugin zip and connect a USD store - [MCP](./mcp.md) - connect a named server to Checkout invoice tools - [Mobile App](./mobile-app.md) - install FinCobra Checkout on iPhone or Android - [Invoices](./invoices.md) - create invoices and read invoice state - [Payment Page](./payment-page.md) - send customers to the hosted payment page - [Donate Button](./donate-button.md) - add a public donation page and embeddable button - [Webhooks](./webhooks.md) - receive and verify payment notifications - [API Reference](./api-reference.md) - endpoint contracts and field reference - [Smart Contracts](./smart-contracts.md) - EVM contract addresses and purpose --- Source: https://fincobra.com/docs/checkout/invoices.html # Invoices Invoices are the core checkout object. Each invoice represents one USD payment request. Use the dashboard to configure payment methods and create an API key. Use the API key from your server to create invoices and read invoice state. ## Create an Invoice ``` POST /api/checkout/invoices ``` Creates an invoice using the payment methods configured in the dashboard. The response includes an invoice `id` and `paymentUrl`. Store both values with your order. Send the customer to `paymentUrl` to complete payment on the hosted payment page. ```bash curl -X POST https://fincobra.com/api/checkout/invoices \ -H "X-Api-Key: fc_live_..." \ -H "Content-Type: application/json" \ -d '{ "amountUsd": 49.99, "productName": "Pro Plan - Monthly", "merchantReference": "order_123", "customerEmail": "buyer@example.com", "metadata": { "orderId": "order_123" } }' ``` ### Request Body | Field | Type | Required | Description | | ------------------- | -------- | -------- | ---------------------------------------------------------------------------------------------------------------- | | `amountUsd` | `number` | Yes | Amount in USD. Minimum `0.01`. | | `paymentTiming` | `object` | No | Override the dashboard default timing for this invoice. | | `productName` | `string` | No | Product or service description. Max 200 chars. | | `issuedBy` | `string` | No | Organization issuing the invoice. Defaults to the dashboard issuer name. Max 120 chars. | | `billTo` | `string` | No | Customer or company name shown on the invoice. Max 120 chars. | | `merchantReference` | `string` | No | Your order or internal reference. Max 200 chars. | | `customerId` | `string` | No | Your internal customer identifier. Max 200 chars. | | `customerEmail` | `string` | No | Customer email for reconciliation. Max 320 chars. | | `redirectUrl` | `string` | No | Override the dashboard redirect URL for this invoice. | | `metadata` | `object` | No | Arbitrary JSON object attached to the invoice. `metadata.notes`, when present, must be a string up to 140 chars. | ### Invoice Timing Immediate invoices expire after a configured number of minutes: ```json { "paymentTiming": { "mode": "immediate", "expiresAfterMinutes": 20 } } ``` Due-date invoices remain payable through the due date plus grace period: ```json { "paymentTiming": { "mode": "due_date", "dueAfterDays": 7, "gracePeriodDays": 14 } } ``` Due-date invoices require an enabled USDT or USDC payment method. Bitcoin payment methods are only used for immediate invoices. ### Response FinCobra returns HTTP `201` with the invoice object. Most integrations only need these fields: ```json { "id": "a1b2c3d4-1111-4222-8333-abcdefabcdef", "paymentUrl": "https://fincobra.com/pay/a1b2c3d4-1111-4222-8333-abcdefabcdef", "amountUsd": 49.99, "status": "awaiting_payment", "paymentCoverage": "no_payment", "receivedAmountUsd": 0, "confirmedAmountUsd": 0, "productName": "Pro Plan - Monthly", "merchantReference": "order_123", "customerEmail": "buyer@example.com", "metadata": { "orderId": "order_123" }, "paymentTiming": { "mode": "immediate", "expiresAfterMinutes": 20, "payableUntilAt": "2026-04-29T10:20:00.000Z" }, "paymentSummary": { "paymentCoverage": "no_payment", "receivedAmountUsd": 0, "confirmedAmountUsd": 0, "remainingAmountUsd": 49.99, "overpaymentAmountUsd": 0 } } ``` The full response also includes payment options and timestamps. See [API Reference](./api-reference.md#create-invoice) for the complete contract. ## Get Invoice Detail ``` GET /api/checkout/invoices/:id ``` Returns a known invoice by `id`. Use this when you need the current invoice state outside a webhook handler. ```bash curl https://fincobra.com/api/checkout/invoices/a1b2c3d4-1111-4222-8333-abcdefabcdef \ -H "X-Api-Key: fc_live_..." ``` Short example: ```json { "id": "a1b2c3d4-1111-4222-8333-abcdefabcdef", "paymentUrl": "https://fincobra.com/pay/a1b2c3d4-1111-4222-8333-abcdefabcdef", "status": "payment_detected", "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "confirmations": 1, "paymentDetectedAt": "2026-04-29T10:05:00.000Z", "confirmedAt": null, "merchantReference": "order_123", "paymentSummary": { "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "remainingAmountUsd": 0, "overpaymentAmountUsd": 0 }, "payments": [] } ``` ## Get Invoice Status ``` GET /api/checkout/invoices/:id/status ``` Use this endpoint when you need a smaller payload for status polling. ```bash curl https://fincobra.com/api/checkout/invoices/a1b2c3d4-1111-4222-8333-abcdefabcdef/status \ -H "X-Api-Key: fc_live_..." ``` ```json { "status": "payment_detected", "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "confirmations": 1, "paymentDetectedAt": "2026-04-29T10:05:00.000Z", "confirmedAt": null, "paidOutOfBandAt": null, "paidOutOfBandNote": null, "paymentSummary": { "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "remainingAmountUsd": 0, "overpaymentAmountUsd": 0 }, "lastPaymentObservedAt": "2026-04-29T10:05:00.000Z" } ``` Webhooks are preferred over polling for production order updates. Polling is useful for admin screens, fallback checks, and local testing. ## Status Handling | Status | Meaning | Recommended merchant action | | ------------------ | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | `awaiting_payment` | No sufficient payment has been detected yet. | Keep the order pending. | | `partially_paid` | Some funds were detected, but the invoice is not fully paid. | Keep the order pending and review `paymentSummary`. | | `payment_detected` | Sufficient funds were detected but have not reached the confirmation threshold. | Keep the order pending unless your business accepts detected-but-unconfirmed payment. | | `confirmed` | Sufficient funds reached the confirmation threshold. | Mark the order paid. | | `paid_out_of_band` | The merchant recorded an external payment for the invoice. | Mark the order paid and keep the recorded note for reconciliation. | | `expired` | The final payable deadline passed before sufficient payment was detected. | Stop accepting payment for this invoice and prompt the customer to create a new checkout. | | `voided` | The invoice was voided before payment was detected. | Stop accepting payment for this invoice. | ## Payment Timing Model The invoice payable window decides whether payment is on time. A payment first observed at or before `paymentTiming.payableUntilAt` is on time; a payment first observed after that timestamp is late. Payment events can still be observed and confirmed after the payable window, but a late payment does not automatically make an `expired` or `voided` invoice paid. On-time full payment moves the invoice to `payment_detected` and then `confirmed` after the configured confirmation threshold. For Bitcoin, the post-expiry confirmation grace applies only when the full quoted Bitcoin amount was observed at or before `payableUntilAt`; it gives that on-time Bitcoin payment more time to confirm. It does not make late first payments or late top-ups valid. If the payable deadline passes while only a partial payment has been observed, the invoice becomes `expired` with `paymentCoverage: "partial_payment"` and opens a `partial_payment` exception. If a full or extra payment arrives after the payable window, the invoice stays `expired` or `voided`, `paymentCoverage` reflects the observed amount, and a `late_payment` exception opens for merchant review. Exception actions have fixed invoice outcomes: | Action | When available | Final invoice state | | ----------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | `accept_late_payment` | `late_payment` exception on an `expired` or `voided` invoice after full payment confirmation. | `confirmed`; the exception is closed. | | `mark_paid_out_of_band` | Open `late_payment` or `partial_payment` exception when the merchant received payment outside FinCobra. | `paid_out_of_band`; the exception is closed and the note is stored on the invoice. | | `close_unpaid` | Open exception on an `expired` or `voided` invoice that should remain unpaid. | The invoice stays `expired` or `voided`; the exception is closed. | Overpayment during the payable window does not open an exception. The invoice follows the normal payment lifecycle and shows `paymentCoverage: "overpayment"` plus the excess amount in `paymentSummary.overpaymentAmountUsd`. ## Void an Invoice ``` POST /api/checkout/invoices/:id/void ``` Voids an `awaiting_payment` invoice. Voided invoices are terminal and are no longer payable. ```bash curl -X POST https://fincobra.com/api/checkout/invoices/a1b2c3d4-1111-4222-8333-abcdefabcdef/void \ -H "X-Api-Key: fc_live_..." \ -H "Content-Type: application/json" \ -d '{}' ``` ## Record Payment ``` POST /api/checkout/invoices/:id/record-payment ``` Records an external payment for an `awaiting_payment`, `partially_paid`, or `expired` invoice. ```bash curl -X POST https://fincobra.com/api/checkout/invoices/a1b2c3d4-1111-4222-8333-abcdefabcdef/record-payment \ -H "X-Api-Key: fc_live_..." \ -H "Content-Type: application/json" \ -d '{ "note": "Paid by wire transfer" }' ``` The `note` field is required and can be up to 2000 characters. The updated invoice has `status: "paid_out_of_band"`, `paidOutOfBandAt`, and `paidOutOfBandNote`. `status` is the fulfillment state for the invoice. `paymentCoverage` explains whether the invoice has no payment, a partial payment, an exact payment, or an overpayment. Overpayment does not open an exception; the invoice remains in its payment lifecycle state and the excess amount is shown in `paymentSummary.overpaymentAmountUsd`. `receivedAmountUsd`, `confirmedAmountUsd`, and `paymentSummary` are USD-denominated. Use `payments[]` when you need payment-level details such as transaction hash, observed amount, confirmations, and `confirmedAt`. ## Payment Options Invoices include `paymentOptions[]` for hosted and custom payment pages. A standard hosted-page integration can ignore this field and redirect customers to `paymentUrl`. Solana wallet payment options use `railType: "address_transfer"` and `network: "solana"` because the connected wallet signs a direct SPL-token transfer to your configured recipient wallet. The payer opens the hosted page in a browser with Phantom, Solflare, Backpack, or another compatible injected Solana wallet, reviews the quoted USDT or USDC transfer, and approves the payment from that wallet. FinCobra records invoice volume and commission internally for billing. Use `paymentOptions[]` only when you are building custom payment UI. See [API Reference](./api-reference.md#payment-option-object) for the field contract. --- Source: https://fincobra.com/docs/checkout/mcp.html # Checkout MCP Connect an AI client to your Checkout account with a named MCP server. You select the tools that each server can use. Each server has its own public URL and OAuth authorization. ## Create a server 1. Sign in to [Checkout](https://fincobra.com). 2. Open **Settings → MCP**. 3. Create a server. Enter a name and select the tools to enable. 4. Copy the server URL. The URL has this form: ```text https://fincobra.com/api/checkout/mcp/{serverId} ``` The URL identifies the server. The client must sign in with OAuth before it can use account tools. Keep tokens private. ## Connect an AI client In ChatGPT, add an MCP plugin with the copied server URL. Select **OAuth** as the authentication method. Start the connection, sign in to FinCobra, and review the requested tools. Approve access to connect the client. A client can show the sign-in prompt when you install the plugin or when you first use it. The client refreshes its authorization automatically. If authorization expires or you revoke it, sign in again. For other clients, add a remote MCP server with the same URL. The client must support Streamable HTTP and OAuth with S256 PKCE. Use the client's connection control to sign in and approve access. Ask the client to run `get_connection_status` to check the connection. A connection in ChatGPT does not guarantee a connection in Codex or another client. Connect each client that you use. ## Tools Only the tools selected for the named server are available to the client. | Tool | Input | Function | | ----------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | | `get_connection_status` | None | Check the account and server connection. | | `get_invoice` | `invoiceId` (UUID) | Read one invoice and its payment status. | | `create_invoice` | `amountUsd`, `idempotencyKey`; optional `productName`, `billTo`, `merchantReference`, `customerEmail` | Create an invoice and return its hosted payment URL. | Use the same `idempotencyKey` when you retry an invoice creation request. ## Change access Open **Settings → MCP** to change the server name or enabled tools. A client can require a tool refresh or a new connection before it shows the changed tool list. Approve access again if the client asks. Delete a server to stop access through its URL. Revoke a connection to stop that client's access. Read tools can expose private account data. Enable write tools only when the client must change account data, and review each requested action. ## OAuth endpoints Clients discover authorization from the server's protected resource metadata: ```text https://fincobra.com/.well-known/oauth-protected-resource/api/checkout/mcp/{serverId} ``` The authorization server metadata is at: ```text https://fincobra.com/.well-known/oauth-authorization-server ``` The authorization server provides these endpoints on the same origin: - Authorization: `/api/mcp/authorize` - Token exchange and refresh: `/api/mcp/token` - Client registration: `/api/mcp/register` - Token revocation: `/api/mcp/revoke` The authorization page opens at `/mcp/connect`. Access is bound to the account, product, and named server that the user approves. --- Source: https://fincobra.com/docs/checkout/mobile-app.html # FinCobra Mobile App FinCobra Checkout is a web app that can be installed to your iPhone or Android home screen. It opens like a mobile app and gives you quick access to invoices, checkout settings, billing, and API keys without installing anything from an app store. Use this guide when you want app-like access to the Checkout dashboard from a phone or tablet. ## Install on iPhone 1. Open [FinCobra Checkout](https://fincobra.com/checkout) in Safari. 2. Tap the **Share** button in the Safari toolbar. 3. Tap **Add to Home Screen**. 4. Tap **Add**. After installation, open FinCobra from the new home-screen icon. It will launch directly into the web app. ## Install on Android 1. Open [FinCobra Checkout](https://fincobra.com/checkout) in Chrome. 2. Tap the browser menu. 3. Tap **Install app** or **Add to Home screen**. 4. Tap **Install** or **Add**. After installation, open FinCobra from the new home-screen icon. Chrome will keep it connected to your browser session. ## What You Can Do From Mobile The mobile web app supports the same Checkout dashboard workflows as the browser version: - Create and review invoices. - Update checkout settings. - Review billing. - Manage API keys. - Check payment status while away from your desktop. For server-side integrations, keep API keys in your backend environment. Do not store checkout API keys in mobile notes, screenshots, or client-side code. ## Troubleshooting If you do not see **Add to Home Screen** or **Install app**, check these items: - Use Safari on iPhone or Chrome on Android. - Make sure you are opening `https://fincobra.com/checkout`. - Refresh the page after signing in. - Confirm that your browser is not in private or incognito mode. - If the app opens signed out later, sign in again from the installed icon. The installed icon is a shortcut to the FinCobra web app. Updates ship automatically, so you do not need to update it from an app store. --- Source: https://fincobra.com/docs/checkout/payment-page.html # Payment Page Each invoice has a public payment page that customers can use to complete their payment. No authentication is required. ## Hosted Payment URL ``` https://fincobra.com/pay/:id ``` Where `:id` is the invoice UUID returned when you create an invoice. The invoice API returns this URL as `paymentUrl`. Store it with your order and redirect the customer to it when they choose to pay. ## Features The hosted payment page includes: - Payment instructions for the selected method - Wallet connection for supported Ethereum and Solana payment methods, including injected Solana wallets such as Phantom, Solflare, and Backpack - Bitcoin QR codes and address-transfer copy buttons for address-transfer rails - Live status updates while the invoice is active - Invoice identity fields such as `issuedBy` and `billTo` when set - Immediate invoice countdowns and due-date invoice timing - Redirect to your configured `redirectUrl` when the payer-facing flow completes ## Linking customers to the page The invoice API returns `paymentUrl`. Link customers directly to that URL: ```html Pay now ``` The hosted page is intended to open directly in the browser. It is not designed for iframe embedding. Solana payments require a Solana wallet in the current browser. On desktop, use a supported wallet extension. On mobile, open the payment URL in the wallet's in-app browser. The page connects directly to the detected Solana wallet before asking the payer to approve the exact USDT or USDC transfer. ## Redirects Set a default redirect URL in the dashboard, or pass `redirectUrl` when creating an invoice. When the payer-facing flow completes, the hosted page redirects the customer to that URL. Do not use the redirect as your only proof of payment. Use verified webhooks, or fetch the invoice status from your server, before fulfilling the order. ## Building a custom payment page Most integrations should use the hosted payment page. If you need a custom payment page, use the public payment-page API documented in [API Reference](./api-reference.md#public-payment-page-api). The public payment-page API is unauthenticated because it powers customer-facing invoice pages. Do not put private merchant metadata, internal customer IDs, or secrets in customer-facing invoice fields. --- Source: https://fincobra.com/docs/checkout/sdk.html # TypeScript SDK Use the FinCobra TypeScript SDK when you want a typed server-side client for the Checkout API. The SDK covers the standard hosted checkout integration: - Create checkout sessions and invoices - Fetch invoice detail and payment status - Verify webhook signatures - Use exported TypeScript types for invoices, payment status, and webhook events Install from GitHub until the package is published to npm: ```bash npm install github:dexstandard/fincobra-sdk ``` Source: [dexstandard/fincobra-sdk](https://github.com/dexstandard/fincobra-sdk) ## Client setup Create the client on your server. Never expose the FinCobra API key or webhook signing secret in browser code. ```ts import { createFinCobraClient } from '@fincobra/sdk'; const fincobra = createFinCobraClient({ apiKey: process.env.FINCOBRA_API_KEY, }); ``` The SDK defaults to `https://fincobra.com`. ## Create checkout `checkoutSessions.create` creates a hosted checkout invoice and returns `paymentUrl`. ```ts const session = await fincobra.checkoutSessions.create({ amountUsd: 49.99, productName: 'Pro Plan - Monthly', merchantReference: 'order_123', customerEmail: 'buyer@example.com', redirectUrl: 'https://example.com/account/billing', metadata: { orderId: 'order_123', }, }); return Response.redirect(session.paymentUrl); ``` ## Create invoice ```ts const invoice = await fincobra.invoices.create({ amountUsd: 25, productName: 'One-time setup', customerEmail: 'buyer@example.com', }); ``` ## Read payment status ```ts const payment = await fincobra.payments.get( 'a1b2c3d4-1111-4222-8333-abcdefabcdef', ); if (payment.status === 'confirmed') { // fulfill the order } ``` You can also call `fincobra.invoices.get(id)` for full invoice detail or `fincobra.invoices.getStatus(id)` for the same status payload as `payments.get(id)`. ## Next.js example ```ts import { createFinCobraClient } from '@fincobra/sdk'; import { NextResponse } from 'next/server'; const fincobra = createFinCobraClient({ apiKey: process.env.FINCOBRA_API_KEY, }); export async function POST() { const invoice = await fincobra.checkoutSessions.create({ amountUsd: 49.99, productName: 'Pro Plan - Monthly', merchantReference: 'order_123', customerEmail: 'buyer@example.com', redirectUrl: 'https://example.com/account/billing', metadata: { orderId: 'order_123', }, }); return NextResponse.json({ invoiceId: invoice.id, paymentUrl: invoice.paymentUrl, }); } ``` ## Express example ```ts import express from 'express'; import { createFinCobraClient } from '@fincobra/sdk'; const app = express(); app.use(express.json()); const fincobra = createFinCobraClient({ apiKey: process.env.FINCOBRA_API_KEY, }); app.post('/checkout', async (req, res, next) => { try { const invoice = await fincobra.invoices.create({ amountUsd: Number(req.body.amountUsd), productName: String(req.body.productName), merchantReference: String(req.body.orderId), customerEmail: typeof req.body.customerEmail === 'string' ? req.body.customerEmail : undefined, metadata: { orderId: String(req.body.orderId), }, }); res.status(201).json({ invoiceId: invoice.id, paymentUrl: invoice.paymentUrl, }); } catch (err: unknown) { next(err); } }); ``` ## Webhook handler example Configure this route with the raw request body. Verify the signature before processing the event, then fetch the latest invoice state before fulfilling an order. ```ts import express from 'express'; import { createFinCobraClient } from '@fincobra/sdk'; const app = express(); const fincobra = createFinCobraClient({ apiKey: process.env.FINCOBRA_API_KEY, }); app.post( '/webhooks/fincobra', express.raw({ type: 'application/json' }), async (req, res, next) => { try { const event = fincobra.webhooks.verifySignature({ body: req.body, signature: req.header('X-Checkout-Signature'), secret: process.env.FINCOBRA_WEBHOOK_SECRET ?? '', }); const invoice = await fincobra.invoices.get(event.invoice.id); if ( invoice.status === 'confirmed' || invoice.status === 'paid_out_of_band' ) { await markOrderPaid(invoice.merchantReference, invoice.id); } res.sendStatus(204); } catch (err: unknown) { next(err); } }, ); ``` ## Error handling All SDK errors extend `FinCobraError`. API responses with `{ "error": "message" }` become `FinCobraApiError` with `statusCode`, `responseBody`, and a clear `message`. ```ts import { FinCobraApiError } from '@fincobra/sdk'; try { await fincobra.invoices.create({ amountUsd: 49.99 }); } catch (err: unknown) { if (err instanceof FinCobraApiError) { err.statusCode; err.message; } throw err; } ``` For endpoint contracts and field definitions, see the [API Reference](./api-reference.md). --- Source: https://fincobra.com/docs/checkout/smart-contracts.html # Smart Contracts FinCobra Checkout uses EVM smart contracts for self-custodial stablecoin invoice payments on Ethereum mainnet, Arbitrum One, and Base. The contracts do not custody merchant funds. Browser-wallet payments route ERC-20 tokens from the payer to the merchant recipient, and address-transfer invoices use deterministic receiver addresses that merchants settle from their own wallet. Only the contract deployments listed on this page are current for FinCobra Checkout. ## Supported EVM Networks | Network | Chain ID | Assets | Contract rails | | ---------------- | -------: | ---------- | ------------------------------------------------ | | Ethereum mainnet | `1` | USDT, USDC | `CheckoutPaymentHubV2`, `InvoiceReceiverFactory` | | Arbitrum One | `42161` | USDC | `CheckoutPaymentHubV2`, `InvoiceReceiverFactory` | | Base | `8453` | USDC | `CheckoutPaymentHubV2`, `InvoiceReceiverFactory` | Solana wallet checkout and Bitcoin xpub-derived address transfers do not require an EVM smart contract deployment. ## Production Deployments | Network | Contract | Address | Transaction | | ---------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | Ethereum mainnet | `CheckoutPaymentHubV2` | [`0x6bf112f72dd7bd0841342ef406546d50d97604db`](https://etherscan.io/address/0x6bf112f72dd7bd0841342ef406546d50d97604db) | [`0x6c346eb9a68ef372790bd4f6ca695410420d9cb6de4cb3a03654103e21912f53`](https://etherscan.io/tx/0x6c346eb9a68ef372790bd4f6ca695410420d9cb6de4cb3a03654103e21912f53) | | Ethereum mainnet | `InvoiceReceiverFactory` | [`0x2bd7c7f7aa3550dbf38c92e0344ae9f503076764`](https://etherscan.io/address/0x2bd7c7f7aa3550dbf38c92e0344ae9f503076764) | [`0x477a3907635db944f3d6506821b1f03ba51dd3ab7c1f0dcd1ccd528de6bc48a9`](https://etherscan.io/tx/0x477a3907635db944f3d6506821b1f03ba51dd3ab7c1f0dcd1ccd528de6bc48a9) | | Arbitrum One | `CheckoutPaymentHubV2` | [`0x3e4a76590ef64d216e360c8cb00731892bbbe3e2`](https://arbiscan.io/address/0x3e4a76590ef64d216e360c8cb00731892bbbe3e2) | [`0x99c6ca9708d4a65ecf0011ce426aa63962c3688632d32697ed8a39e61440795a`](https://arbiscan.io/tx/0x99c6ca9708d4a65ecf0011ce426aa63962c3688632d32697ed8a39e61440795a) | | Arbitrum One | `InvoiceReceiverFactory` | [`0xd1780c8e7fbc9d6289ebfd7acea7e1e685d02d7b`](https://arbiscan.io/address/0xd1780c8e7fbc9d6289ebfd7acea7e1e685d02d7b) | [`0x766f367e71e8f619b8e267ca82f0fb72e6666aa2d07c9e7051a6ef4d042ff878`](https://arbiscan.io/tx/0x766f367e71e8f619b8e267ca82f0fb72e6666aa2d07c9e7051a6ef4d042ff878) | | Base | `CheckoutPaymentHubV2` | [`0xef53ac785d9d5104d46d56bc997dd1af06899f9f`](https://basescan.org/address/0xef53ac785d9d5104d46d56bc997dd1af06899f9f) | [`0xbbe3e4195578714103d638fc7df8321c45c29660f2c9c6796838311a0586f21a`](https://basescan.org/tx/0xbbe3e4195578714103d638fc7df8321c45c29660f2c9c6796838311a0586f21a) | | Base | `InvoiceReceiverFactory` | [`0x982dcd79cd8b267ca9303048f3c4cf4c846f34a4`](https://basescan.org/address/0x982dcd79cd8b267ca9303048f3c4cf4c846f34a4) | [`0x06dcc02859e164eefe15504ffb1c2661b676210920103d882400d593a7cc2379`](https://basescan.org/tx/0x06dcc02859e164eefe15504ffb1c2661b676210920103d882400d593a7cc2379) | ## Development Deployments | Network | Contract | Address | Transaction | | ---------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | Ethereum mainnet | `CheckoutPaymentHubV2` | [`0xdbb84fac2cdd5dd57dfa50b043f776a63ee70667`](https://etherscan.io/address/0xdbb84fac2cdd5dd57dfa50b043f776a63ee70667) | [`0x58f79ae5c56bd981c04cd2dbef0f0d43048b40e24065c850686dcc2acf029875`](https://etherscan.io/tx/0x58f79ae5c56bd981c04cd2dbef0f0d43048b40e24065c850686dcc2acf029875) | | Ethereum mainnet | `InvoiceReceiverFactory` | [`0xbaf95f53bbb165a7a3cd579687bb1c5ffbbf4b85`](https://etherscan.io/address/0xbaf95f53bbb165a7a3cd579687bb1c5ffbbf4b85) | [`0x62be318c521c0a3407c95cabc40f248e93dfcbfb61f82d48782b82b33c613ba8`](https://etherscan.io/tx/0x62be318c521c0a3407c95cabc40f248e93dfcbfb61f82d48782b82b33c613ba8) | | Arbitrum One | `CheckoutPaymentHubV2` | [`0xfcdea5f5695057878c3f8b1d77e68cd5be50a4d2`](https://arbiscan.io/address/0xfcdea5f5695057878c3f8b1d77e68cd5be50a4d2) | [`0xc62cb6d70771545785ccfcbee8d718b27241224c3deb6298b448eacf9cb43ae0`](https://arbiscan.io/tx/0xc62cb6d70771545785ccfcbee8d718b27241224c3deb6298b448eacf9cb43ae0) | | Arbitrum One | `InvoiceReceiverFactory` | [`0x5bdfc56d7cf67bc866602e061f70c1bd65bb9b0b`](https://arbiscan.io/address/0x5bdfc56d7cf67bc866602e061f70c1bd65bb9b0b) | [`0xcc7fcb069427bc5ae29ae89648f33265f3b3de073df8baa0fc5a57478cfbcab6`](https://arbiscan.io/tx/0xcc7fcb069427bc5ae29ae89648f33265f3b3de073df8baa0fc5a57478cfbcab6) | | Base | `CheckoutPaymentHubV2` | [`0x10d492bb50d927a14a406766a60bd353c456bb3a`](https://basescan.org/address/0x10d492bb50d927a14a406766a60bd353c456bb3a) | [`0x37b78a2a6be385b3d4be289b824d2460c83ba3cec710764176b904786f024b5e`](https://basescan.org/tx/0x37b78a2a6be385b3d4be289b824d2460c83ba3cec710764176b904786f024b5e) | | Base | `InvoiceReceiverFactory` | [`0x005c5bc30cede75d9e452ec709962f8f94af92fa`](https://basescan.org/address/0x005c5bc30cede75d9e452ec709962f8f94af92fa) | [`0x9d6e7aca4d6d99d6c9c2fe5c6ac30df971463ec0cf0f6cf8ebd53381869782ce`](https://basescan.org/tx/0x9d6e7aca4d6d99d6c9c2fe5c6ac30df971463ec0cf0f6cf8ebd53381869782ce) | ## Supported Tokens | Network | Token | Address | | ---------------- | ----- | ------------------------------------------------------------------------------------------------------------ | | Ethereum mainnet | USDT | [`0xdAC17F958D2ee523a2206206994597C13D831ec7`](https://etherscan.io/address/0xdAC17F958D2ee523a2206206994597C13D831ec7) | | Ethereum mainnet | USDC | [`0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48`](https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) | | Arbitrum One | USDC | [`0xaf88d065e77c8cC2239327C5EDb3A432268e5831`](https://arbiscan.io/address/0xaf88d065e77c8cC2239327C5EDb3A432268e5831) | | Base | USDC | [`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`](https://basescan.org/address/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) | Arbitrum USDT0 is not supported. ## Contract Roles `CheckoutPaymentHubV2` is the browser-wallet payment rail. Customers connect an EVM wallet on the selected network and submit the payment transaction themselves. The hub verifies the requested token is allowlisted, transfers the merchant and commission amounts atomically, and emits a `PaymentAccepted` event with invoice identifiers used by FinCobra Checkout to detect payment. `InvoiceReceiverFactory` is the address-transfer rail. It derives per-invoice receiver addresses before deployment, which lets customers pay from an exchange or wallet without connecting to the hosted checkout page. When settlement is needed, the merchant wallet deploys the receiver and sweeps the designated token to the configured merchant recipient. ## Deployment Policy FinCobra Checkout fund-path contracts are immutable deployments, not proxies. A behavior change requires a new deployment and a new deployment manifest. The contracts do not include implementation addresses, UUPS upgradeability markers, role-manager contracts, or standalone commission-manager contracts. The commission recipient is `0xd2Bd690D56369E50fCc24A4e0c41B9d1a2ECD22f`. The maximum commission cap is `50` bps (`0.5%`) of the gross payment amount. ## Related API Fields Invoice responses include contract data only when the invoice has an EVM wallet-contract payment option. In that case, `paymentOptions[].network` identifies the EVM network, `paymentOptions[].optionPayload.contractAddress` is the active `CheckoutPaymentHubV2` address, `paymentOptions[].optionPayload.tokenContract` is the selected stablecoin contract, and `paymentOptions[].optionPayload.recipient` is the merchant recipient for that invoice. See [API Reference](./api-reference.md#payment-option-object) for the full payment option shape. --- Source: https://fincobra.com/docs/checkout/webhooks.html # Webhooks Configure a webhook URL in the dashboard to receive real-time HTTP POST notifications when invoice status changes. The API settings page lets you turn individual webhook events on or off. ## Destination requirements Use the final public HTTPS endpoint as the webhook URL. Customer webhook URLs must use HTTPS. HTTP is allowed only for local development loopback addresses. Redirect responses are treated as failed deliveries; FinCobra does not follow them. URL credentials and fragments are not accepted. The hostname must resolve only to public IP addresses. Private, loopback, link-local, and reserved addresses are blocked. FinCobra checks DNS on each delivery attempt and connects only to the checked addresses. These rules also apply to retries. ## Events | Event | Trigger | | -------------------------- | ---------------------------------------------------------------------------------------------------------- | | `invoice_created` | Invoice was created | | `invoice_partially_paid` | Some payment was detected, but total detected funds are below the required amount | | `invoice_payment_detected` | Sufficient payment was detected, but observed funds have not reached the configured confirmation threshold | | `invoice_confirmed` | Sufficient detected funds reached the configured confirmation threshold | | `invoice_expired` | Final payable deadline elapsed before sufficient payment was detected | | `invoice_voided` | Invoice was voided before payment was detected | | `invoice_payment_recorded` | External payment was recorded for the invoice | | `invoice_exception_opened` | An exception was opened for merchant review | | `invoice_exception_closed` | An open exception was closed by the merchant | ## Payload All webhook payloads have the same shape. The `invoice` object contains the latest invoice state at the time the event is sent. ```json { "event": "invoice_payment_detected", "invoice": { "id": "a1b2c3d4-...", "amountUsd": 49.99, "status": "payment_detected", "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "confirmations": 0, "paymentDetectedAt": "2026-04-29T10:05:00.000Z", "confirmedAt": null, "paidOutOfBandAt": null, "paidOutOfBandNote": null, "defaultPaymentOptionId": "payment-option-default", "lastPaymentOptionId": "payment-option-1", "paymentSummary": { "paymentCoverage": "exact_payment", "receivedAmountUsd": 49.99, "confirmedAmountUsd": 0, "remainingAmountUsd": 0, "overpaymentAmountUsd": 0 }, "lastTransactionHash": "def456...", "exceptionType": null, "exceptionStatus": null, "exceptionAction": null, "exceptionNote": null, "exceptionClosedAt": null, "metadata": { "orderId": "order_123" } } } ``` Webhook payloads do not include full payment instructions. Use `lastPaymentOptionId` to identify the latest observed payment option for the event, and fetch the invoice from your server if you need the latest full invoice detail. ## Processing Webhooks Use webhooks as a notification that invoice state changed. After verifying the signature, fetch the latest invoice from your server before making fulfillment decisions: ```bash curl https://fincobra.com/api/checkout/invoices/a1b2c3d4-1111-4222-8333-abcdefabcdef \ -H "X-Api-Key: fc_live_..." ``` This keeps your order logic correct if events are retried, delayed, or delivered close together. Webhook delivery is at-least-once. Your endpoint may receive the same event more than once. Events for the same invoice are delivered independently, so do not rely on receiving every intermediate status before a later status. Webhook payloads do not include a separate webhook event ID or event timestamp. For idempotency, use the invoice ID together with the event name, current invoice status, relevant invoice timestamps, and `lastTransactionHash` when present. Recommended order handling: | Invoice state | Recommended action | | ------------------ | ----------------------------------------------------------------------------------------- | | `confirmed` | Mark the order paid if it is not already paid. | | `paid_out_of_band` | Mark the order paid and store the payment note for reconciliation. | | `payment_detected` | Keep the order pending unless your business accepts detected-but-unconfirmed payment. | | `partially_paid` | Keep the order pending and review `paymentSummary.remainingAmountUsd`. | | `expired` | Stop accepting payment for this invoice and prompt the customer to create a new checkout. | | `voided` | Stop accepting payment for this invoice. | If `paymentCoverage` is `overpayment`, the invoice can still be `confirmed`. Overpayment does not open an exception; use `paymentSummary.overpaymentAmountUsd` for your own operations flow. Exceptions are opened only for `partial_payment` and `late_payment`. A payment first observed at or before `paymentTiming.payableUntilAt` is on time; a payment first observed after that timestamp is late. Late payments keep the invoice `expired` or `voided` until the merchant accepts the late payment. Closing an exception records `exceptionStatus: "closed"`, `exceptionAction`, `exceptionNote`, and `exceptionClosedAt` on merchant-facing invoice and webhook payloads. `confirmedAmountUsd` is the USD amount that has reached the confirmation threshold. Use `payments[]` on the invoice detail response when you need payment-level asset amounts, USD credit, transaction hash, and confirmation details. ## Signature verification Create a webhook signing secret in the dashboard before relying on webhook delivery. Every webhook request includes an `X-Checkout-Signature` header containing an HMAC-SHA256 hex digest of the raw JSON body, signed with your webhook secret. Copy the current webhook secret from the dashboard when it is created or rotated. To verify: ```javascript import crypto from 'node:crypto'; function verifySignature(body, signature, secret) { if (typeof signature !== 'string') { return false; } if (!/^[0-9a-f]{64}$/i.test(signature)) { return false; } const expected = crypto .createHmac('sha256', secret) .update(body) .digest('hex'); const signatureBuffer = Buffer.from(signature, 'hex'); const expectedBuffer = Buffer.from(expected, 'hex'); return ( signatureBuffer.length === expectedBuffer.length && crypto.timingSafeEqual(signatureBuffer, expectedBuffer) ); } // In your webhook handler: const body = req.rawBody; // raw JSON string const signature = req.headers['x-checkout-signature']; const webhookSecret = 'your-webhook-secret'; // store the dashboard secret server-side if (!verifySignature(body, signature, webhookSecret)) { return res.status(401).send('Invalid signature'); } ``` ```python import hmac import hashlib def verify_signature(body: bytes, signature: str, secret: str) -> bool: expected = hmac.new( secret.encode(), body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected) ``` ::: tip Your webhook signing secret is only shown when it is created or rotated. Store it securely and never expose it publicly. ::: ## Retry policy If your endpoint returns a non-2xx status or the request times out (10 seconds), the webhook is retried: | Attempt | Delay after failure | | --------- | ------------------- | | 1st retry | 1 minute | | 2nd retry | 5 minutes | | 3rd retry | 15 minutes | After 3 failed attempts, the webhook is permanently marked as failed and not retried again. Each invoice event is delivered independently. Use the dashboard Webhooks page to search delivery records by delivery ID, invoice ID, event, URL, or HTTP response code. Filter by status or event when reviewing failed deliveries. ## Best practices - **Respond quickly** — return a 2xx status within 10 seconds. Process the event asynchronously if needed. - **Verify signatures** — always validate the `X-Checkout-Signature` header before processing. - **Fetch current state** — after verification, read `GET /api/checkout/invoices/:id` from your server and make decisions from the current invoice. - **Handle duplicates** — make order updates idempotent. Store processed transitions using the invoice ID, event name, status, relevant timestamps, and transaction hash when present. - **Use HTTPS** — your webhook URL must use HTTPS in production. --- Source: https://fincobra.com/docs/checkout/woocommerce.html # WooCommerce The FinCobra WooCommerce plugin adds BTC, USDT, and USDC as a payment option in your store. Checkout happens on FinCobra's hosted payment page, and confirmed payments update the matching WooCommerce order automatically. Payments settle directly to the wallet configured in your FinCobra account. ## Requirements - WordPress 6.6 or later and WooCommerce 9.0 or later. Current WooCommerce 11 requires WordPress 6.9 or later. - A WordPress site with WooCommerce already active. - A WooCommerce store using USD as its store currency. - A FinCobra account with at least one supported receiving wallet. - Either the **Annual** or **Commission** WooCommerce plan. - HTTPS on the public store. The first release supports normal product purchases through classic checkout and WooCommerce Checkout Blocks. It does not advertise support for automatic refunds, WooCommerce Subscriptions, or pre-orders. The plugin is not listed on WordPress.org and does not include an auto-updater. Install updates by downloading a new `fincobra-woocommerce.zip` from a GitHub Release and uploading it again. WordPress.org does not deliver plugin updates. ## Install the plugin 1. Activate WooCommerce first. 2. Download `fincobra-woocommerce.zip` from [GitHub Releases (latest)](https://github.com/dexstandard/fincobra-woocommerce/releases/latest). There is no WordPress.org listing or auto-updater. 3. In WordPress, open **Plugins → Add New → Upload Plugin**. 4. Choose `fincobra-woocommerce.zip` and install it. 5. Activate **FinCobra for WooCommerce**. The store currency must be **USD**, and the public store must use HTTPS. ## Pricing | Plan | Subscription | FinCobra commission | | --- | --- | --- | | Annual | $99 per year | 0% | | Commission | None | 0.5% of confirmed order volume | Both plans include the same gateway, supported networks, hosted checkout, and automatic order updates. Unpaid, voided, and expired invoices do not count as commission volume. ## Connect a Store 1. Create a FinCobra account and open [WooCommerce](https://fincobra.com/woocommerce). After you sign in, the **Connect your store** page is shown. 2. Choose a plan. Pay the annual invoice before connecting if you chose Annual. 3. Open **Checkout settings** and configure a receiving wallet. 4. Create a Checkout API key. It is used only once to establish the store-specific connection. 5. If the plugin is not installed yet, follow [Install the plugin](#install-the-plugin). 6. Open **WooCommerce → Settings → Payments → FinCobra**. If that Payments table is blank, open `/wp-admin/admin.php?page=wc-settings&tab=checkout§ion=fincobra`. 7. Paste the Merchant API key and click **Connect**. The plugin connects the store and shows **Connected**. Connecting does not select a billing plan. 8. If the settings screen asks you to choose a plan, open [WooCommerce](https://fincobra.com/woocommerce) and choose **Annual** or **Commission**, then return to the plugin. 9. Enable the payment method and save the WooCommerce settings. The plugin exchanges the Checkout API key for a credential scoped to that store. The connection also creates a unique webhook signing secret. The raw store credential and webhook secret are not displayed in the FinCobra dashboard after creation. ## Customer Payment Flow 1. The customer chooses FinCobra and places the WooCommerce order. 2. The plugin creates one idempotent FinCobra invoice and stores its identifier on the WooCommerce order. 3. WooCommerce places the order on hold and redirects the customer to the hosted payment page. 4. FinCobra monitors the selected blockchain payment. 5. A signed webhook notifies the store when invoice state changes. 6. The plugin fetches the authoritative invoice and marks the order paid only after confirmation. Returning from the hosted page does not mark an order paid. Payment status comes from the signed webhook and the authenticated invoice lookup. ## Order Statuses | FinCobra invoice | WooCommerce action | | --- | --- | | `awaiting_payment` | Keep the order on hold | | `partially_paid` | Keep the order on hold | | `payment_detected` | Keep the order on hold until confirmation | | `confirmed` | Call WooCommerce payment completion | | `paid_out_of_band` | Call WooCommerce payment completion | | `expired` | Mark the order failed so the customer can retry | | `voided` | Mark the order failed | The plugin uses WooCommerce order APIs and is compatible with High-Performance Order Storage. ## Reliability Webhook processing is idempotent. Duplicate events do not complete an order twice. Before changing an order, the plugin verifies the webhook signature, fetches the current invoice, and checks its store installation, order reference, currency, and amount. WooCommerce Action Scheduler periodically reconciles orders that remain on hold, covering temporary webhook or store availability failures. ## Troubleshooting ### The Payments tab is blank WooCommerce 11 can show a React Payments table that stays empty when JavaScript is blocked. Open `/wp-admin/admin.php?page=wc-settings&tab=checkout§ion=fincobra` to reach the FinCobra settings screen. ### Place Order fails after connecting Connecting a store does not select a billing plan. Choose **Annual** or **Commission** at [https://fincobra.com/woocommerce](https://fincobra.com/woocommerce), then try the order again. ### FinCobra is not offered at checkout Confirm that: - The WooCommerce store currency is USD. - The gateway is enabled. - The FinCobra connection reports **Connected**. - The WooCommerce plan is active. - The order contains only supported product types. ### An order remains on hold Open the order notes and the FinCobra invoice. A detected payment remains on hold until it reaches the required confirmation threshold. The reconciliation job will also retry the authenticated status check. ### Reconnect or rotate credentials Disconnect the store from the FinCobra WooCommerce page, then connect it again from the plugin settings. Disconnecting invalidates the store credential and stops new invoice creation for that installation. ## Data Sent to FinCobra For a WooCommerce payment, the plugin sends the order identifier, USD total, product summary, return URL, and the customer fields enabled by the integration. FinCobra uses this information to create the payment invoice, reconcile the order, deliver payment events, and calculate the selected plan's billing. See the [Privacy Policy](https://fincobra.com/privacy) and [Terms](https://fincobra.com/terms). --- Source: https://fincobra.com/docs/ ## Documentation for agents Start with the [Agent Quickstart](./checkout/agent-quickstart.md) to integrate hosted checkout. Use the [API reference](./checkout/api-reference.md) for request and response details, and the [MCP guide](./checkout/mcp.md) to connect an agent to an authorized account. The [Markdown index](./llms.txt) lists all documentation pages. The [complete Markdown reference](./llms-full.txt) combines them in one file. Request a public page with `Accept: text/markdown` to receive its Markdown version. These files are generated from the same documentation source as the HTML pages.