# Quickstart

> Accept your first USDC payment in about five minutes: create an API key, create an invoice, send the customer to the hosted checkout, and fulfill on the invoice.paid webhook.

Part of the Soca documentation (https://soca.finance/docs). Human version: https://soca.finance/docs/developers/quickstart
Soca is closer to Stripe Payment Links than to a card API. Your backend creates a payable object: a one-time **invoice** or a **subscription plan**, and Soca returns a hosted, non-custodial `checkout_url`. You send the customer there, they pay in USDC (gas is sponsored, so they never pay network fees), and the money settles directly to your wallet in the same transaction. You never build payment UI, and you fulfill the order from a signed webhook.

> **Amounts are USDC base units**: Every amount in the API is an integer in USDC base units: USDC on Solana (base units, 6 decimals: $9.00 = 9000000). In JavaScript: `Math.round(dollars * 1e6)`. Amounts come back as strings.

1. **Get an API key**: In the [Dashboard](/dashboard), open **Developers** and create a secret key. Keys are shown once, so store it in your server environment (for example `SOCA_SECRET_KEY`). Start with a test key. It begins with `sk_test_` and everything it creates stays in test mode. See [Authentication](/docs/developers/authentication).
2. **Create an invoice**: From your backend, POST to `/v1/invoices` with an amount (base units) and a description. The response includes a hosted `checkout_url`. Use raw HTTP or the official [@usesoca/sdk](/docs/developers/sdk). Both examples are below.
3. **Send the customer to checkout_url**: Link, redirect, or email the customer to the `checkout_url`. The hosted page handles everything: they connect a Solana wallet or sign in with email/Google (an embedded wallet is created for them), approve once, and pay in USDC with no gas. No money moves until they pay.
4. **Receive the invoice.paid webhook and fulfill**: Register an HTTPS endpoint in **Dashboard → Developers → Webhooks** and you get a `whsec_` signing secret. When the customer pays, Soca POSTs an `invoice.paid` event to your endpoint. Verify the `x-wc-signature` header with one SDK line, then fulfill the order. Details in [Webhooks and events](/docs/developers/webhooks).
5. **Go live**: Swap `sk_test_` for an `sk_live_` key. Nothing else changes: same endpoints, same fields, same webhooks. Test and live data are fully isolated from each other.

## Create an invoice with curl

**POST /v1/invoices**

```bash
curl -X POST https://soca.to/v1/invoices \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"amount":"2500000","description":"Acme Pro, March","invoice_number":"INV-1041"}'
```

**Response: 201 Created**

```json
{
  "object": "invoice",
  "id": "inv_...",
  "amount": "2500000",
  "status": "open",
  "checkout_url": "https://soca.to/invoice/inv_..."
}
```

## Create an invoice with @usesoca/sdk

**npm i @usesoca/sdk**

```typescript
import { Soca } from '@usesoca/sdk';

const soca = new Soca(process.env.SOCA_SECRET_KEY);

const invoice = await soca.invoices.create({
  amount: 2_500_000, // $2.50 in USDC base units
  description: 'Acme Pro, March',
  invoice_number: 'INV-1041',
});

// Send the customer here. The page is fully hosted.
console.log(invoice.checkout_url);
```

## Verify the webhook and fulfill

Soca signs every delivery with HMAC-SHA256 over the raw body. Verify before trusting anything, using the raw (unparsed) request body:

**Webhook handler (Express-style)**

```typescript
import { verifyWebhookSignature } from '@usesoca/sdk';

app.post('/webhooks/soca', express.raw({ type: 'application/json' }), (req, res) => {
  const ok = verifyWebhookSignature(
    process.env.SOCA_WEBHOOK_SECRET, // whsec_...
    req.body.toString('utf8'),       // the RAW body
    req.headers['x-wc-signature'],
  );
  if (!ok) return res.status(400).end();

  const event = JSON.parse(req.body.toString('utf8'));
  if (event.type === 'invoice.paid') {
    // Fulfill the order for event.data
  }
  res.status(200).end();
});
```

> **Fulfill on invoice.paid, not on redirect**: The webhook is the source of truth. There is no `payment.confirmed` event. One-time purchases are fulfilled on `invoice.paid`, and subscription access starts on `subscription.active`. See the full event list in [Webhooks and events](/docs/developers/webhooks).

## Where to next

- [The Soca SDK](/docs/developers/sdk): every namespace of the official TypeScript client.
- [API reference](/docs/developers/api-reference): every endpoint, field, and response.
- [Webhooks and events](/docs/developers/webhooks): signatures, retries, and the full event table.
- [Errors and testing](/docs/developers/errors-and-testing): error codes, test mode, idempotency, pagination.
- [MCP for AI agents](/docs/developers/mcp): let an AI client drive the API over the Model Context Protocol.
