# Быстрый старт

> Создайте проект, проверьте инвойс и подписанный вебхук в тестовом режиме, затем выпустите рабочие ключи и примите первый реальный платёж в основной сети.

## Создайте аккаунт

Войдите на [paymos.io](/_public) по ссылке-входу из письма (или через Google / passkey) — пароль не нужен.

## Создайте проект

Откройте [**Дашборд → Проекты → Создать**](/projects/create), укажите название и включите пары активов и сетей, которыми будут платить клиенты.

Здесь же вы выбираете **способ подключения**: REST API, Low-Code, CMS-плагин, Терминал или Telegram-бот. По умолчанию — REST API. Выбор один и навсегда: у готового проекта способ не меняется, под другой заводите отдельный проект.

Дальше от него зависит, что вообще откроется. Экран терминала работает только в проекте «Терминал», сниппет Low-Code SDK — только в «Low-Code», панель CMS-плагина — только в «CMS-плагин». Запрос на инвойс из чужого способа подключения возвращает `403`. Остальные шаги ниже написаны под REST API.

## Получите тестовые API-ключи

Перейдите в **Дашборд → Разработчик → API-ключи** и сгенерируйте тестовую пару ключей:

:::dash-list
- **Payment Key** (`pk_test_…`) — используется для создания тестовых инвойсов и чтения статуса
- **API Secret** (`sk_test_…`) — секрет для подписи запроса HMAC-SHA256; см. предупреждение ниже
:::

:::warning
API Secret никогда не покидает ваш сервер. Не передавайте его в заголовках, строке запроса или клиентском коде. Не коммитьте в git — при утечке немедленно ротируйте.
:::

## Зарегистрируйте тестовый адрес вебхука

Откройте обработчик вебхука по HTTPS, затем в тестовом режиме перейдите в [**Дашборд → Разработчик → Вебхуки**](/developers/webhooks) и создайте адрес. Укажите HTTPS URL обработчика, выберите категорию событий «Инвойсы» и свой проект. После создания сразу сохраните на сервере однократно показанный секрет `whsec_test_…`: он не входит в пару API-ключей и подписывает тестовые вебхуки.

## Создайте тестовый инвойс

Используйте официальный серверный SDK из вкладок ниже. Он сериализует запрос,
создаёт точную HMAC-подпись, проставляет заголовки с меткой времени, безопасно повторяет
запросы и возвращает типизированный ответ. Ручная подпись описана отдельно в
[Аутентификации](/ru/docs/authentication.md).

```curl
API_KEY_ID="pk_live_xxxxxxxxxxxx"
API_SECRET="sk_live_xxxxxxxxxxxx"
BODY='{"project_id":"prj_xxxxxxxxxxxx","amount":"100.00","currency":"USD","external_order_id":"order-123","client_id":"customer-456"}'

TS=$(date +%s)
BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | sed 's/^.* //')
SIGNATURE=$(printf '%s\n%s\n%s\n%s\n%s' "$TS" POST /v1/invoices '' "$BODY_HASH" \
  | openssl dgst -sha256 -hmac "$API_SECRET" -binary | base64)

curl -sS https://api.paymos.io/v1/invoices \
  -H "Authorization: HMAC-SHA256 $API_KEY_ID:$SIGNATURE" \
  -H "X-Request-Timestamp: $TS" \
  -H "Content-Type: application/json" \
  -d "$BODY"
```

```javascript
import { Paymos, externalOrderId } from '@paymos/sdk';

const paymos = new Paymos({
  apiKey: process.env.PAYMOS_API_KEY,
  apiSecret: process.env.PAYMOS_API_SECRET,
});

const invoice = await paymos.invoices.create({
  projectId: 'prj_xxxxxxxxxxxx',
  amount: '100.00',
  currency: 'USD',
  externalOrderId: externalOrderId('order'),
  clientId: 'customer-456',
});

console.log(invoice.invoiceId, invoice.paymentUrl);
```

```python
import os

from paymos import Paymos

paymos = Paymos(
    api_key=os.environ["PAYMOS_API_KEY"],
    api_secret=os.environ["PAYMOS_API_SECRET"],
)
invoice = paymos.invoices.create(
    project_id="prj_xxxxxxxxxxxx",
    amount="100.00",
    currency="USD",
    external_order_id="order-123",
    client_id="customer-456",
)

print(invoice["invoice_id"], invoice["payment_url"])
```

```php
<?php
use Paymos\Client;
use Paymos\ClientConfig;
use Paymos\IdempotencyKey;

$paymos = new Client(new ClientConfig(
    getenv('PAYMOS_API_KEY'),
    getenv('PAYMOS_API_SECRET')
));
$invoice = $paymos->invoices()->create(array(
    'project_id' => 'prj_xxxxxxxxxxxx',
    'amount' => '100.00',
    'currency' => 'USD',
    'external_order_id' => IdempotencyKey::externalOrderId('order'),
    'client_id' => 'customer-456',
));

echo $invoice['invoice_id'] . ' ' . $invoice['payment_url'];
```

```go
package main

import (
	"context"
	"fmt"
	"os"

	paymos "github.com/Paymos-labs/go-sdk/v2"
)

func main() {
	client, err := paymos.NewClient(os.Getenv("PAYMOS_API_KEY"), os.Getenv("PAYMOS_API_SECRET"))
	if err != nil {
		panic(err)
	}
	invoice, err := client.Invoices.Create(context.Background(), paymos.CreateInvoiceParams{
		ProjectID:       "prj_xxxxxxxxxxxx",
		Amount:          "100.00",
		Currency:        "USD",
		ExternalOrderID: "order-123",
		ClientID:        stringPointer("customer-456"),
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(invoice.InvoiceID, invoice.PaymentURL)
}

func stringPointer(value string) *string { return &value }
```

```csharp
using Paymos;

using var paymos = new PaymosClient(
    Environment.GetEnvironmentVariable("PAYMOS_API_KEY")!,
    Environment.GetEnvironmentVariable("PAYMOS_API_SECRET")!);

var invoice = await paymos.Invoices.CreateAsync(new CreateInvoiceRequest(
    ProjectId: "prj_xxxxxxxxxxxx",
    Amount: "100.00",
    Currency: "USD",
    ExternalOrderId: "order-123",
    ClientId: "customer-456"));

Console.WriteLine($"{invoice.InvoiceId} {invoice.PaymentUrl}");
```

```java
import io.paymos.CreateInvoiceRequest;
import io.paymos.Invoice;
import io.paymos.PaymosClient;

PaymosClient paymos = new PaymosClient(
    System.getenv("PAYMOS_API_KEY"),
    System.getenv("PAYMOS_API_SECRET"));

Invoice invoice = paymos.invoices.create(
    CreateInvoiceRequest.builder()
        .projectId("prj_xxxxxxxxxxxx")
        .amount("100.00")
        .currency("USD")
        .externalOrderId("order-123")
        .clientId("customer-456")
        .build());

System.out.println(invoice.invoiceId() + " " + invoice.paymentUrl());
```

```ruby
require 'paymos'

paymos = Paymos::Client.new(
  api_key: ENV.fetch('PAYMOS_API_KEY'),
  api_secret: ENV.fetch('PAYMOS_API_SECRET')
)
invoice = paymos.invoices.create(
  project_id: 'prj_xxxxxxxxxxxx',
  amount: '100.00',
  currency: 'USD',
  external_order_id: 'order-123',
  client_id: 'customer-456'
)

puts "#{invoice.invoice_id} #{invoice.payment_url}"
```

```rust
use paymos::{CreateInvoiceRequest, PaymosClient};

let paymos = PaymosClient::new(
    std::env::var("PAYMOS_API_KEY")?,
    std::env::var("PAYMOS_API_SECRET")?,
)?;
let invoice = paymos
    .invoices()
    .create(&CreateInvoiceRequest {
        project_id: "prj_xxxxxxxxxxxx".to_owned(),
        amount: "100.00".to_owned(),
        currency: "USD".to_owned(),
        external_order_id: "order-123".to_owned(),
        network: None,
        allow_multiple_payments: None,
        customer_fee_percent: None,
        client_id: Some("customer-456".to_owned()),
    })
    .await?;

println!("{} {}", invoice.invoice_id, invoice.payment_url);
```

## Откройте страницу оплаты

В ответе приходит `payment_url`. Перенаправьте клиента по этой ссылке или встройте тот же URL с `?embed=true`:

```json
{
  "invoice_id": "inv_5CcyDYmMUGtzYL10q0Iimr",
  "project_id": "prj_xFukZuAJZR06pLVBh3uwzv",
  "status": "awaiting_client",
  "is_final": false,
  "is_test": true,
  "payment_url": "https://checkout.paymos.io/invoice/inv_5CcyDYmMUGtzYL10q0Iimr",
  "order": {
    "external_id": "order-123",
    "client_id": "customer-456",
    "amount": "100.00",
    "currency": "USD"
  },
  "created_at": 1743594000,
  "updated_at": 1743594000,
  "expires_at": 1743597600
}
```

Клиент видит страницу оплаты с адресом кошелька, QR-кодом и таймером. Оплатить можно любым активом и любой сетью, включёнными в проекте, — см. [Поддерживаемые валюты](/ru/docs/supported-currencies.md).

Если канал проекта — «Telegram-бот», эта ссылка приводит клиента в бота Paymos. Плательщик выбирает там актив и сеть, получает адрес и QR-код и следит за статусом, не выходя из Telegram, — и аккаунт Paymos ему не нужен.

См. [Платёжную страницу](/ru/docs/hosted-checkout.md) — режим перенаправления, встроенный режим (iframe) и обработка событий браузера.

## Обработайте вебхук

Когда платёж подтверждён, Paymos отправляет POST на зарегистрированный URL вебхука:

```json
{
  "event_id": "evt_J7EEYeL9pZJukfj2c5OQ44",
  "event_type": "invoice.paid",
  "version": 1,
  "occurred_at": 1739281200,
  "data": {
    "invoice_id": "inv_5CcyDYmMUGtzYL10q0Iimr",
    "project_id": "prj_xFukZuAJZR06pLVBh3uwzv",
    "status": "paid",
    "is_final": true,
    "is_test": false,
    "payment_url": "https://checkout.paymos.io/invoice/inv_5CcyDYmMUGtzYL10q0Iimr",
    "order": {
      "external_id": "order-12345",
      "client_id": "customer-67890",
      "amount": "100.00",
      "currency": "USD"
    },
    "payment": {
      "currency": "USDT",
      "network": "TRC20",
      "chain_id": 728126428,
      "contract_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
      "expected": "50.00",
      "address": "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
      "exchange_rate": "2.00",
      "paid": "50.00",
      "remaining": "0",
      "fee": "0.50",
      "net": "49.50",
      "transfers": [
        {
          "tx_hash": "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
          "amount": "50.00",
          "status": "confirmed",
          "created_at": 1739281020,
          "confirmed_at": 1739281200,
          "required_confirmations": 19,
          "estimated_confirmation_at": 1739281080,
          "explorer_url": "https://tronscan.org/#/transaction/abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
        }
      ]
    },
    "expires_at": 1739284200,
    "completed_at": 1739281200,
    "created_at": 1739277600,
    "updated_at": 1739281200
  }
}
```

Проверьте заголовок `X-Webhook-Signature` и выполните заказ.

:::danger
Никогда не выдавайте товар по событию из браузера (`paymos:succeeded` или iframe `postMessage`). Источник истины — только подписанный вебхук на стороне сервера. Сигналы из браузера легко подделать.
:::

```javascript
import { WebhookVerifier } from '@paymos/sdk';

const verifier = new WebhookVerifier(process.env.PAYMOS_WEBHOOK_SECRET);

// Keep the body as a Buffer until verification succeeds.
const event = verifier.constructEvent(signatureHeader, rawBody);
console.log(event.eventId, event.eventType, event.data);
```

```python
import os

from paymos import WebhookVerifier

verifier = WebhookVerifier(os.environ["PAYMOS_WEBHOOK_SECRET"])

# Keep raw_body as bytes until verification succeeds.
event = verifier.construct_event(signature_header, raw_body)
print(event["event_id"], event["event_type"], event["data"])
```

```php
<?php
use Paymos\Webhook\WebhookEvent;
use Paymos\Webhook\WebhookVerifier;

$verifier = new WebhookVerifier(getenv('PAYMOS_WEBHOOK_SECRET'));

// Keep $rawBody unchanged until verification succeeds.
$payload = $verifier->decodeVerifiedPayload($signatureHeader, $rawBody);
$event = new WebhookEvent($payload);
echo $event->id() . ' ' . $event->type();
```

```go
package main

import (
	"fmt"
	"os"
	"time"

	paymos "github.com/Paymos-labs/go-sdk/v2"
)

type invoiceEventData struct {
	InvoiceID string `json:"invoice_id"`
}

func handleWebhook(signatureHeader string, rawBody []byte) error {
	verifier, err := paymos.NewWebhookVerifier(os.Getenv("PAYMOS_WEBHOOK_SECRET"), 5*time.Minute)
	if err != nil {
		return err
	}
	var event paymos.WebhookEvent[invoiceEventData]
	if err := verifier.ConstructEvent(signatureHeader, rawBody, time.Now(), &event); err != nil {
		return err
	}
	fmt.Println(event.EventID, event.EventType, event.Data.InvoiceID)
	return nil
}
```

```csharp
using Paymos;

var verifier = new WebhookVerifier(
    Environment.GetEnvironmentVariable("PAYMOS_WEBHOOK_SECRET")!);

// Keep rawBody as ReadOnlySpan<byte> until verification succeeds.
var webhook = verifier.ConstructEvent<InvoiceEventData>(signatureHeader, rawBody);
Console.WriteLine($"{webhook.EventId} {webhook.EventType} {webhook.Data.InvoiceId}");

public sealed record InvoiceEventData(string InvoiceId);
```

```java
import com.fasterxml.jackson.databind.JsonNode;
import io.paymos.WebhookEvent;
import io.paymos.WebhookVerifier;
import java.time.Instant;

WebhookVerifier verifier = new WebhookVerifier(System.getenv("PAYMOS_WEBHOOK_SECRET"));

// Keep rawBody as byte[] until verification succeeds.
WebhookEvent<JsonNode> event =
    verifier.constructEvent(signatureHeader, rawBody, Instant.now());
System.out.println(event.eventId() + " " + event.eventType() + " " + event.data());
```

```ruby
require 'paymos'

verifier = Paymos::WebhookVerifier.new(ENV.fetch('PAYMOS_WEBHOOK_SECRET'))

# Keep raw_body as the original String until verification succeeds.
event = verifier.construct_event(signature_header, raw_body)
puts "#{event.event_id} #{event.event_type} #{event.data}"
```

```rust
use paymos::{Invoice, WebhookEvent, WebhookVerifier};

let verifier = WebhookVerifier::new(std::env::var("PAYMOS_WEBHOOK_SECRET")?)?;

// Keep raw_body as &[u8] until verification succeeds.
let event: WebhookEvent<Invoice> = verifier.construct_event(signature_header, raw_body)?;
println!("{} {} {}", event.event_id, event.event_type, event.data.invoice_id);
```

## Запустите симуляцию тестового платежа

Через [симулятор платежа](/ru/docs/invoices/simulate-payment.md) отметьте тестовый инвойс оплаченным. Не отправляйте реальные активы на тестовый инвойс. Убедитесь, что зарегистрированный адрес получил подписанное событие, а сервер проверил его с помощью `whsec_test_…` до исполнения заказа.

## Перейдите к реальным платежам

Когда тестовый запрос и подписанный вебхук работают, переключитесь в рабочий режим в дашборде. Ручное согласование не требуется. Само переключение не создаёт ключи: откройте **Дашборд → Разработчик → API-ключи** и создайте отдельные рабочие ключи (`pk_live_…` и `sk_live_…`). Зарегистрируйте рабочий адрес вебхука отдельно и храните его секрет `whsec_live_…` отдельно от тестового.

## Примите первый реальный платёж в основной сети

Повторите запрос инвойса с рабочими ключами, откройте полученный `payment_url` — он ведёт на платёжную страницу или в бота Paymos, смотря какой канал у проекта, — и отправьте поддерживаемый актив в одной из включённых основных сетей. Paymos обнаружит перевод и отправит подписанный рабочий вебхук после необходимых подтверждений.

Обычный самостоятельный путь от регистрации до первого реального платежа в основной сети занимает около 10 минут. Это ориентир для подключения, а не SLA: время подтверждения зависит от сети, суммы и текущего состояния блокчейна.

## Следующие шаги

:::dash-list
- [API-ключи](/ru/docs/api-keys.md) — типы, области действия, жизненный цикл
- [Серверные SDK](/ru/docs/server-sdks.md) — официальные клиенты для восьми языковых экосистем
- [Платёжная страница](/ru/docs/hosted-checkout.md) — перенаправление и встроенный режим (iframe)
- [Тестирование](/ru/docs/testing.md) — как работать в тестовом режиме
- [Вебхуки](/ru/docs/webhooks.md) — проверка подписи и политика повторов
:::
