# Libraries

Official libraries wrap the API for Node.js, Python, PHP, Go and Dart. They
all do the same things: create and read payments, list the coins a customer
can pay with, manage your webhook (IPN) URL, verify webhook notifications in
one call, and call the public checkout API. Field names are the API's own, so
everything in these docs applies. Each library has a full reference page.

| Language | Reference | Install | Import | Needs |
|---|---|---|---|---|
| Node.js / TypeScript | [Node.js library](/docs/sdk-node) | `npm install @payitgo/node` | `import { Payitgo } from "@payitgo/node"` | Node 18+, no dependencies |
| Python | [Python library](/docs/sdk-python) | `pip install payitgo` | `import payitgo` | Python 3.8+, no dependencies |
| PHP | [PHP library](/docs/sdk-php) | `composer require payitgo/payitgo-php` | `use Payitgo\Client;` | PHP 8.1+, curl and json |
| Go | [Go library](/docs/sdk-go) | `go get github.com/payitgo/go-sdk` | `import payitgo "github.com/payitgo/go-sdk"` | Go 1.21+, no dependencies |
| Dart / Flutter | [Dart and Flutter library](/docs/sdk-dart) | `dart pub add payitgo` | `import 'package:payitgo/payitgo.dart';` | Dart 3, `http` and `crypto` |

The Python package is published as **`payitgo`** and imported as `payitgo`.

A WooCommerce shop needs no code: the [WooCommerce plugin](/docs/sdk-woocommerce)
adds crypto to its checkout and marks orders paid by itself.

Use the libraries on your server: they need your secret key. The
exceptions need no key, only the payment's `checkout_token` from your server:
Dart's [`PayitgoCheckout`](#flutter-apps), made for Flutter apps, and the
[React checkout widget](/docs/sdk-react), which shows the payment in a modal
in a React app.

## Creating a client

Every client takes your secret key and the same options. The key decides the
mode: a test key works with test payments and the test webhook and its
secret, a live key with live ones. Use the webhook secret of the same
mode. A missing key is refused when the client is created, before any
request. The key's text does not say its mode: ask payitgo with `isLive()`
(the first call reads it, later ones use the answer).

| Option | Node | Python | PHP | Go | Dart |
|---|---|---|---|---|---|
| Constructor | [`new Payitgo(key, {…})`](/docs/sdk-node#new-payitgo) | [`payitgo.Client(key, …)`](/docs/sdk-python#payitgoclient) | [`new \Payitgo\Client($key, [...])`](/docs/sdk-php#new-client) | [`payitgo.NewClient(key, …)`](/docs/sdk-go#newclient) | [`Payitgo(key, …)`](/docs/sdk-dart#payitgo) |
| Base URL, `https://api.payitgo.co` | `baseUrl` | `base_url` | `'base_url'` | `payitgo.WithBaseURL(u)` | `baseUrl` |
| Base URL from the environment | `PAYITGO_BASE_URL` | `PAYITGO_BASE_URL` | `PAYITGO_BASE_URL` | `PAYITGO_BASE_URL` | No: pass it yourself |
| Timeout per attempt (default 30 s) | `timeoutMs` | `timeout` (seconds) | `'timeout'` (seconds) | The HTTP client's `Timeout`, and your `ctx` | `timeout` (`Duration`) |
| Retries (default 2) | `maxRetries` | `max_retries` | `'max_retries'` | `payitgo.WithMaxRetries(n)` | `maxRetries` |
| Your own HTTP client | No | No | No | `payitgo.WithHTTPClient(h)` | `httpClient` |
| Webhook secret | `webhookSecret` | `webhook_secret` | `'webhook_secret'` | `payitgo.WithWebhookSecret(s)` | `webhookSecret` |
| Default `webhook_url` for payments | `webhookUrl` | `webhook_url` | `'webhook_url'` | `payitgo.WithWebhookURL(u)` | `webhookUrl` |
| Test or live, asked of payitgo | `await payitgo.isLive()` | `client.is_live()` | `$payitgo->isLive()` | `client.IsLive(ctx)` | `await payitgo.isLive()` |
| A bad key or no address | Throws `Error` | Raises `ValueError` | Throws `InvalidArgumentException` | Returns an error | Throws `ArgumentError` |

Without a base URL option, the libraries use `PAYITGO_BASE_URL` (except Dart),
then the gateway address they were released with.

## Errors and retries

| | Node | Python | PHP | Go | Dart |
|---|---|---|---|---|---|
| A failed API call | [`PayitgoError`](/docs/sdk-node#payitgoerror) | [`payitgo.PayitgoError`](/docs/sdk-python#payitgoerror) | [`Payitgo\Exception\PayitgoException`](/docs/sdk-php#payitgoexception) | [`*payitgo.Error`](/docs/sdk-go#payitgoerror) | [`PayitgoException`](/docs/sdk-dart#payitgoexception) |
| Its HTTP status | `.status` | `.status` | `->status` | `.Status` | `.status` |
| Its message | `.message` | `.message` | `->getMessage()` | `.Message` | `.message` |
| A notification that fails verification | [`SignatureVerificationError`](/docs/sdk-node#signatureverificationerror) | [`payitgo.SignatureVerificationError`](/docs/sdk-python#signatureverificationerror) | [`Payitgo\Exception\SignatureVerificationException`](/docs/sdk-php#signatureverificationexception) | [`payitgo.ErrSignature`](/docs/sdk-go#payitgoerrsignature) (`errors.Is`) | [`SignatureVerificationException`](/docs/sdk-dart#signatureverificationexception) |

- **The status** is the API's HTTP status, or `0` when no answer came (a
  network error or a timeout). The message is the API's `error` message
  ([Errors and limits](/docs/errors)).
- **Retries.** Network errors and timeouts, `429` and `5xx` are retried up to
  twice (the retries option), after 0.5 s and then 1 s. Other statuses are
  never retried. Every call is safe to repeat: creating a payment again for
  the same `order_id` returns its open payment, choosing a coin that was
  already chosen answers `409`, and setting or removing the webhook URL twice
  changes nothing.

## Webhooks

Every library manages the webhook of the key's mode (`get`, `set`, `remove`,
`test`) and verifies notifications in constant time: it checks the
`X-Webhook-Signature` against the **raw** body, refuses a timestamp more than
5 minutes from your clock, and returns the parsed event
([how it works](/docs/webhooks#verifying-the-signature)).

| | Node | Python | PHP | Go | Dart |
|---|---|---|---|---|---|
| With the client's webhook secret | `payitgo.webhooks.constructEvent(body, sig)` | `client.webhooks.construct_event(body, sig)` | `$payitgo->webhooks->constructEvent($body, $sig)` | `client.Webhooks.ConstructEvent(body, sig)` | `payitgo.webhooks.constructEvent(body, sig)` |
| With another secret | `…(body, sig, secret)` | `…(body, sig, secret)` | `…($body, $sig, $secret)` | `…(body, sig, secret)` | `…(body, sig, secret: s)` |
| Without a client | `Payitgo.webhooks.constructEvent(body, sig, secret)` | `payitgo.Webhook.construct_event(body, sig, secret)` | `\Payitgo\Webhook::constructEvent($body, $sig, $secret)` | `payitgo.ConstructEvent(body, sig, secret)` | `Webhook.constructEvent(body, sig, secret)` |
| Another tolerance | 4th argument, seconds | `tolerance=`, seconds | 4th argument, seconds | `payitgo.ConstructEventWithTolerance(…, d)` | `tolerance:`, a `Duration` |
| The raw body | `express.raw(…)`, then `req.body` | `request.get_data()` (Flask) | `file_get_contents('php://input')` | `io.ReadAll(r.Body)` | `await request.readAsString()` (shelf) |
| A full handler | [Express](/docs/sdk-node#a-webhook-ipn-handler-with-express) | [Flask and FastAPI](/docs/sdk-python#a-webhook-ipn-handler-with-flask) | [plain PHP](/docs/sdk-php#a-webhook-ipn-handler-in-plain-php), [Laravel](/docs/sdk-php#a-webhook-ipn-handler-in-laravel) | [net/http](/docs/sdk-go#a-webhook-ipn-handler-with-nethttp) | [shelf](/docs/sdk-dart#a-webhook-ipn-handler-with-shelf) |

## Reference

| Call | HTTP | Returns |
|---|---|---|
| `payments.create({...})` | `POST /v1/payments` | The [payment](/docs/payments#the-payment-object). Takes every [create parameter](/docs/payments#parameters), `webhook_url` included |
| `payments.retrieve(id)` | `GET /v1/payments/{id}` | The payment, by payment id or your order id |
| `payments.selectToken(id, token)` | `POST /v1/payments/{id}/token` | The whole payment, with the coin the customer picked on [your own page](/docs/checkout#your-own-payment-page): address, amount, QR payload, deadline ([details](/docs/payments#choose-the-coin-later)) |
| `payments.simulate(id, outcome)` | `POST /v1/payments/{id}/simulate` | Test keys only: the payment, with the [outcome you chose](/docs/testing#outcomes) started |
| `payments.list({status, chain, token, store_id, limit, offset})` | `GET /v1/payments` | A page of payments, newest first |
| `tokens.list({amount_usd?})` | `GET /v1/tokens` | The [tokens](/docs/payments#the-tokeninfo-object) your customers can pay with; with an amount, the ones a payment of it offers, with `amount_due_usd` ([details](/docs/payments#list-tokens)) |
| `webhooks.get()` | `GET /v1/webhook` | The [webhook](/docs/webhooks#the-webhook-object): `url` (may be null) and `secret` |
| `webhooks.set(url)` | `PUT /v1/webhook` | The webhook, now pointing at `url`. Safe to retry |
| `webhooks.remove()` | `DELETE /v1/webhook` | The webhook with `url` null; the secret is kept |
| `webhooks.test()` | `POST /v1/webhook/test` | The [delivery](/docs/webhooks#the-delivery-object) of a signed `ping` |
| `webhooks.constructEvent(rawBody, signature, secret?)` | none (local) | The verified event, or a signature error |
| `checkout.retrieve(checkout_token)` | `GET /checkout/{checkout_token}` | The payer's view, without the key (for pages with no server: [public checkout API](/docs/checkout#the-public-checkout-api)) |
| `checkout.selectToken(checkout_token, token)` | `POST /checkout/{checkout_token}/token` | The payer's view with its coin chosen, without the key |

The same calls in each language:

| Call | Node | Python | PHP | Go | Dart |
|---|---|---|---|---|---|
| Create a payment | [`payments.create({…})`](/docs/sdk-node#paymentscreate) | [`payments.create(order_id, amount_usd, …)`](/docs/sdk-python#paymentscreate) | [`payments->create([…])`](/docs/sdk-php#payments-create) | [`Payments.Create(ctx, &PaymentParams{…})`](/docs/sdk-go#paymentscreate) | [`payments.create(orderId: …, amountUsd: …)`](/docs/sdk-dart#paymentscreate) |
| Read one | [`payments.retrieve(id)`](/docs/sdk-node#paymentsretrieve) | [`payments.retrieve(id)`](/docs/sdk-python#paymentsretrieve) | [`payments->retrieve($id)`](/docs/sdk-php#payments-retrieve) | [`Payments.Get(ctx, id)`](/docs/sdk-go#paymentsget) | [`payments.retrieve(id)`](/docs/sdk-dart#paymentsretrieve) |
| Simulate a test payment | [`payments.simulate(id, outcome)`](/docs/sdk-node#paymentssimulate) | [`payments.simulate(payment_id, outcome)`](/docs/sdk-python#paymentssimulate) | [`payments->simulate($id, $outcome)`](/docs/sdk-php#payments-simulate) | [`Payments.Simulate(ctx, id, outcome)`](/docs/sdk-go#paymentssimulate) | [`payments.simulate(id, outcome)`](/docs/sdk-dart#paymentssimulate) |
| List | [`payments.list({…})`](/docs/sdk-node#paymentslist) | [`payments.list(…)`](/docs/sdk-python#paymentslist) | [`payments->list([…])`](/docs/sdk-php#payments-list) | [`Payments.List(ctx, &ListParams{…})`](/docs/sdk-go#paymentslist) | [`payments.list(…)`](/docs/sdk-dart#paymentslist) |
| Choose the coin | [`payments.selectToken(id, token)`](/docs/sdk-node#paymentsselecttoken) | [`payments.select_token(payment_id, token)`](/docs/sdk-python#paymentsselect-token) | [`payments->selectToken($id, $token)`](/docs/sdk-php#payments-selecttoken) | [`Payments.SelectToken(ctx, id, token)`](/docs/sdk-go#paymentsselecttoken) | [`payments.selectToken(id, token)`](/docs/sdk-dart#paymentsselecttoken) |
| List tokens | [`tokens.list({amount_usd})`](/docs/sdk-node#tokenslist) | [`tokens.list(amount_usd=…)`](/docs/sdk-python#tokenslist) | [`tokens->list($amountUsd)`](/docs/sdk-php#tokens-list) | [`Tokens.List(ctx, &TokenListParams{…})`](/docs/sdk-go#tokenslist) | [`tokens.list(amountUsd: …)`](/docs/sdk-dart#tokenslist) |
| Read the webhook | [`webhooks.get()`](/docs/sdk-node#webhooksget) | [`webhooks.get()`](/docs/sdk-python#webhooksget) | [`webhooks->get()`](/docs/sdk-php#webhooks-get) | [`Webhooks.Get(ctx)`](/docs/sdk-go#webhooksget) | [`webhooks.get()`](/docs/sdk-dart#webhooksget) |
| Set its URL | [`webhooks.set(url)`](/docs/sdk-node#webhooksset) | [`webhooks.set(url)`](/docs/sdk-python#webhooksset) | [`webhooks->set($url)`](/docs/sdk-php#webhooks-set) | [`Webhooks.Set(ctx, url)`](/docs/sdk-go#webhooksset) | [`webhooks.set(url)`](/docs/sdk-dart#webhooksset) |
| Remove its URL | [`webhooks.remove()`](/docs/sdk-node#webhooksremove) | [`webhooks.remove()`](/docs/sdk-python#webhooksremove) | [`webhooks->remove()`](/docs/sdk-php#webhooks-remove) | [`Webhooks.Remove(ctx)`](/docs/sdk-go#webhooksremove) | [`webhooks.remove()`](/docs/sdk-dart#webhooksremove) |
| Send a test ping | [`webhooks.test()`](/docs/sdk-node#webhookstest) | [`webhooks.test()`](/docs/sdk-python#webhookstest) | [`webhooks->test()`](/docs/sdk-php#webhooks-test) | [`Webhooks.Test(ctx)`](/docs/sdk-go#webhookstest) | [`webhooks.test()`](/docs/sdk-dart#webhookstest) |
| Verify a notification | [`webhooks.constructEvent(…)`](/docs/sdk-node#webhooksconstructevent) | [`webhooks.construct_event(…)`](/docs/sdk-python#webhooksconstruct-event) | [`webhooks->constructEvent(…)`](/docs/sdk-php#webhooks-constructevent) | [`Webhooks.ConstructEvent(…)`](/docs/sdk-go#webhooksconstructevent) | [`webhooks.constructEvent(…)`](/docs/sdk-dart#webhooksconstructevent) |
| Payer's view (no key) | [`checkout.retrieve(token)`](/docs/sdk-node#checkoutretrieve) | [`checkout.retrieve(token)`](/docs/sdk-python#checkoutretrieve) | [`checkout->retrieve($token)`](/docs/sdk-php#checkout-retrieve) | [`Checkout.Get(ctx, token)`](/docs/sdk-go#checkoutget) | [`checkout.retrieve(token)`](/docs/sdk-dart#checkoutretrieve) |
| Payer chooses the coin (no key) | [`checkout.selectToken(token, coin)`](/docs/sdk-node#checkoutselecttoken) | [`checkout.select_token(token, coin)`](/docs/sdk-python#checkoutselect-token) | [`checkout->selectToken($token, $coin)`](/docs/sdk-php#checkout-selecttoken) | [`Checkout.SelectToken(ctx, token, coin)`](/docs/sdk-go#checkoutselecttoken) | [`checkout.selectToken(token, coin)`](/docs/sdk-dart#checkoutselecttoken) |

Method names follow each language: `select_token` and `construct_event` in
Python; `Get` (not `retrieve`), `SelectToken`, `Set`, `Remove`, `Test` and
`ConstructEvent` with a `context.Context` in Go. Dart also has
[`checkout.watch`](/docs/sdk-dart#checkoutwatch), a stream of the payment's
status changes that follows late payments (it polls every 5 seconds: the
fallback for apps that cannot use the [socket](/docs/checkout#follow-it-live)),
[`checkout.simulate`](/docs/sdk-dart#checkoutsimulate) for a Flutter app's
test payments (no key), and [`close()`](/docs/sdk-dart#close) for the HTTP
client it made.

## A payment's own webhook URL

Pass `webhook_url` to send one payment's notifications somewhere else
([why](/docs/webhooks#per-payment-webhook-url)), or set the client's default
webhook URL option to do it for every payment that client creates. A payment's
own `webhook_url` wins over the client's default:

```js
const payitgo = new Payitgo(process.env.PAYITGO_STORE_KEY, {
  webhookUrl: "https://eu.acme.example/payitgo/ipn",   // every payment of this client
  webhookSecret: process.env.PAYITGO_WEBHOOK_SECRET,   // the same secret signs them
});
await payitgo.payments.create({ order_id: "EU-77", amount_usd: 20, webhook_url: "https://eu.acme.example/orders/EU-77/ipn" }); // this one only
```

| | Node | Python | PHP | Go | Dart |
|---|---|---|---|---|---|
| One payment | `webhook_url` | `webhook_url=` | `'webhook_url'` | `PaymentParams.WebhookURL` | `webhookUrl:` |
| Every payment of a client | `webhookUrl` | `webhook_url=` | `'webhook_url'` | `payitgo.WithWebhookURL(u)` | `webhookUrl:` |

## Flutter apps

Never ship a secret key in an app. The Dart library's
[`PayitgoCheckout`](/docs/sdk-dart#payitgocheckout) needs no key: your backend
creates the payment and hands the app its `checkout_token`, and the app shows
the payment, lets the customer choose the coin and follows its status
([a full screen](/docs/sdk-dart#a-flutter-payment-screen)). Follow it with the
[socket](/docs/checkout#follow-it-live) (`subscribe` with the checkout token)
for every change as it happens; `checkout.watch` polls every 5 seconds, for
apps that cannot use socket.io:

```dart
final checkout = PayitgoCheckout(baseUrl: 'https://api.payitgo.co');
await for (final p in checkout.watch(checkoutToken)) {
  if (p.isConfirmed) showPaid();
}
```

## By language

### Node.js

[Install](/docs/sdk-node#install) ·
[configuration](/docs/sdk-node#configuration) ·
[errors](/docs/sdk-node#errors) ·
[payments](/docs/sdk-node#payments) ·
[webhooks](/docs/sdk-node#webhooks) ·
[types](/docs/sdk-node#types) ·
[your own payment page](/docs/sdk-node#your-own-payment-page) ·
[test mode](/docs/sdk-node#test-mode)

### Python

[Install](/docs/sdk-python#install) ·
[configuration](/docs/sdk-python#configuration) ·
[errors](/docs/sdk-python#errors) ·
[payments](/docs/sdk-python#payments) ·
[webhooks](/docs/sdk-python#webhooks) ·
[types](/docs/sdk-python#types) ·
[your own payment page](/docs/sdk-python#your-own-payment-page) ·
[test mode](/docs/sdk-python#test-mode)

### PHP

[Install](/docs/sdk-php#install) ·
[configuration](/docs/sdk-php#configuration) ·
[errors](/docs/sdk-php#errors) ·
[payments](/docs/sdk-php#payments) ·
[webhooks](/docs/sdk-php#webhooks) ·
[types](/docs/sdk-php#types) ·
[your own payment page](/docs/sdk-php#your-own-payment-page) ·
[test mode](/docs/sdk-php#test-mode)

### Go

[Install](/docs/sdk-go#install) ·
[configuration](/docs/sdk-go#configuration) ·
[errors](/docs/sdk-go#errors) ·
[payments](/docs/sdk-go#payments) ·
[webhooks](/docs/sdk-go#webhooks) ·
[types](/docs/sdk-go#types) ·
[your own payment page](/docs/sdk-go#your-own-payment-page) ·
[test mode](/docs/sdk-go#test-mode)

### Dart

[Install](/docs/sdk-dart#install) ·
[configuration](/docs/sdk-dart#configuration) ·
[errors](/docs/sdk-dart#errors) ·
[payments](/docs/sdk-dart#payments) ·
[webhooks](/docs/sdk-dart#webhooks) ·
[types](/docs/sdk-dart#types) ·
[your own payment page](/docs/sdk-dart#your-own-payment-page) ·
[Flutter](/docs/sdk-dart#a-flutter-payment-screen) ·
[test mode](/docs/sdk-dart#test-mode)
