# Crypto payments for digital goods, SaaS and online services

> Sell licences, subscriptions and online services for crypto: one API call creates the payment, a signed webhook tells your server when to grant access.

Source: https://cryptolike.net/use-cases/digital-goods
Updated: 2026-09-25

CryptoLike lets a digital business deliver automatically once a crypto payment is confirmed. Your server creates the payment with one API call, the customer pays on the payment page, and a signed webhook tells your server to grant access. Licence keys, top-ups, subscription periods and service credits work this way without anyone checking the blockchain.

## Why do digital products need an API rather than a pay button?

A digital product is delivered by code, not by a person, so its payment has to be machine-readable. The code needs to know that the payment is confirmed, how much arrived and which order it belongs to. It must also deliver exactly once, even when a message is repeated. CryptoLike provides each of these as an API field or a webhook rule.

Without that, access is granted on a transfer that is only detected, a retry creates two deliveries, a reorganisation rolls back a transfer already treated as paid, and float amounts lose precision.

## How CryptoLike fits an automated delivery flow

CryptoLike keeps the flow to three server-side pieces: create a payment, send the customer to it, and handle one webhook. The API secret never leaves your server. Everything your delivery code needs travels with the payment, including your own order id and up to 20 metadata pairs.

1. **Create the payment** with `POST /v1/invoices`: amount, currency (a fiat code such as `USD` or a coin ticker), your `order_id` and optional `metadata`. With an `Idempotency-Key`, a retry within 24 h returns the same payment.
2. **Send the customer** to `checkout_url`, or embed the page with the [checkout widget](https://cryptolike.net/docs/widget) so they stay in your app; the browser gets only the payment id.
3. **Deliver on `invoice.paid`.** Verify the signature, skip an `event_id` you have already processed, grant access and answer 2xx.

```json
{
  "amount": "29.00",
  "currency": "USD",
  "order_id": "sub-8812-2026-10",
  "metadata": { "plan": "pro", "period": "2026-10" },
  "return_url": "https://app.example.com/billing/done"
}
```

`metadata` comes back in the API and in every `invoice.*` webhook. It is never shown to the payer, and it is limited to 20 string pairs and 4 KB.

## What your integration can rely on

CryptoLike publishes its payment contract, so your code can handle every case in advance. The numbers in this table come from the API and webhook documentation. They describe how the platform behaves towards your server, not a promise about how fast a network confirms.

| Rule | Value |
|---|---|
| Webhook answer | Any 2xx within 10 seconds |
| Webhook retries | 1 min, 5 min, 15 min, 1 h, 6 h, 24 h: 7 attempts over about 31 h, with the same `event_id` |
| Signature | HMAC-SHA256 over the timestamp and the raw body, header `X-Signature` |
| Idempotency | `Idempotency-Key` replays the same answer for 24 h |
| Rate limit | Per API key, 600 requests per minute by default; `429` comes with `Retry-After` |
| Amounts | Decimal strings with up to 18 decimals, never floats |
| Test mode | `ck_test_` keys and the test API URL, with test networks; test coins have no value |

Payment statuses are `new`, `pending`, `paid`, `underpaid`, `expired` and `cancelled`; only `paid` means the funds are confirmed and credited. If a reorganisation rolls back a confirmed transfer, you receive `invoice.reverted`: pause delivery until a new `invoice.paid`. See the [payment status model](https://cryptolike.net/docs#status-model).

## How to connect

A CryptoLike integration needs a server that keeps the API secret, a test key and an https endpoint for webhooks. Official SDKs for TypeScript, Python, Go and PHP sign requests, retry, add idempotency keys and verify webhooks. They are pre-release: install them from the repository until they are published to package registries.

1. Ask CryptoLike support for access to the test environment: in its cabinet create a `ck_test_` API key with `create_invoice` and `read`, and use its API URL.
2. Install an SDK: [TypeScript SDK](https://cryptolike.net/integrations/sdk-typescript), [Python SDK](https://cryptolike.net/integrations/sdk-python), [Go SDK](https://cryptolike.net/integrations/sdk-go) or [PHP SDK](https://cryptolike.net/integrations/sdk-php). You can also sign requests yourself as described in [API authentication](https://cryptolike.net/docs/auth).
3. Create a payment and pay it on a test network.
4. Handle the webhook as described in the guide to [verifying crypto payment webhooks](https://cryptolike.net/guides/verify-crypto-payment-webhooks).
5. Switch to a live key.

The full route list is in the [API reference](https://cryptolike.net/docs/api), and the platform fee per asset is on the [pricing page](https://cryptolike.net/pricing).

Selling plans that renew? See [crypto payments for subscriptions](https://cryptolike.net/use-cases/subscriptions): one invoice per billing period, priced in USD.

## FAQ

### When should I grant access: on `pending` or on `paid`?

Grant access on `paid`. `pending` means a transfer is detected and waiting for confirmations, and it can still fail to reach the full amount. `paid` means the full amount is confirmed and credited to your balance.

### How do I avoid delivering twice?

Verify the signature, then look up the `event_id` in your store. If you have seen it, answer 2xx and stop. Delivery is at least once, so repeats are expected. When creating payments, use an `Idempotency-Key` so a retried request never creates a second payment.

### Can I sell subscriptions?

Yes, as one payment per period. The customer sends every crypto transfer, so nothing is debited from their wallet automatically. Create a payment for each period, show or send its link, and put the plan and period in `metadata`.

### What if the customer sends more than the price?

The payment is `paid`, the whole sum is credited to your balance, and the webhook carries `overpaid_amount` greater than zero. You can deliver as usual and settle the difference with the customer.
