Интеграция службы доставки Европочты на сайт
Европочта — белорусская частная служба доставки, специализирующаяся на доставке товаров интернет-магазинов. Работает преимущественно по Беларуси, предоставляет сеть пунктов выдачи, постаматы и курьерскую доставку. Для белорусских e-commerce проектов часто становится основным вариантом из-за развитой сети ПВЗ и понятного API.
Подключение к API
Европочта предоставляет REST API с авторизацией по логину и паролю. Документация доступна после регистрации в системе. Базовый клиент:
class EvropochtaClient
{
private string $baseUrl = 'https://api.europost.by/api/v1';
private ?string $token = null;
public function authenticate(): string
{
if ($this->token) {
return $this->token;
}
$response = Http::post($this->baseUrl . '/auth/login', [
'login' => config('services.europost.login'),
'password' => config('services.europost.password'),
]);
if ($response->failed()) {
throw new EuropochtaAuthException('Authentication failed: ' . $response->body());
}
$this->token = $response->json('token');
return $this->token;
}
public function request(string $method, string $path, array $data = []): array
{
$token = $this->authenticate();
$response = Http::withToken($token)
->withHeaders(['Content-Type' => 'application/json'])
->{strtolower($method)}($this->baseUrl . $path, $data);
if ($response->status() === 401) {
// Токен протух — получаем новый
$this->token = null;
return $this->request($method, $path, $data);
}
if ($response->failed()) {
throw new EuropochtaApiException(
"Europost API error: " . $response->body(),
$response->status()
);
}
return $response->json() ?? [];
}
}
Расчёт стоимости
public function calculateDelivery(
string $fromCityId,
string $toCityId,
float $weightKg,
int $width,
int $height,
int $depth
): array {
$response = $this->request('POST', '/calc', [
'from_city_id' => $fromCityId,
'to_city_id' => $toCityId,
'weight' => (int)ceil($weightKg * 1000), // граммы, округляем вверх
'width' => $width,
'height' => $height,
'depth' => $depth,
]);
return collect($response['services'] ?? [])
->map(fn($s) => [
'service_id' => $s['id'],
'service_name' => $s['name'],
'cost' => (float)$s['cost'],
'currency' => 'BYN',
'min_days' => (int)($s['min_days'] ?? 1),
'max_days' => (int)($s['max_days'] ?? 7),
'to_door' => (bool)($s['to_door'] ?? false),
])
->toArray();
}
Список городов и пунктов выдачи
public function getCities(): array
{
return Cache::remember('europost_cities', now()->addDay(), function () {
return $this->request('GET', '/cities');
});
}
public function getPickupPoints(string $cityId): array
{
$response = $this->request('GET', '/pickup-points', [
'city_id' => $cityId,
]);
return collect($response ?? [])
->map(fn($p) => [
'id' => $p['id'],
'code' => $p['code'],
'name' => $p['name'],
'address' => $p['address'],
'lat' => (float)($p['lat'] ?? 0),
'lng' => (float)($p['lng'] ?? 0),
'work_time' => $p['schedule'] ?? '',
'phone' => $p['phone'] ?? '',
'type' => $p['type'] ?? 'pvz', // pvz, postamat
'cash_allowed'=> (bool)($p['cash'] ?? false),
])
->toArray();
}
Создание заказа
public function createOrder(Order $order): array
{
$payload = [
'order_id' => (string)$order->id,
'service_id' => $order->europost_service_id,
'from_city_id' => config('services.europost.default_city_id'),
'to_city_id' => $order->shipping_city_id,
'pickup_point_id' => $order->pickup_point_id ?? null,
// Данные получателя
'recipient' => [
'name' => $order->recipient_name,
'phone' => preg_replace('/[^0-9+]/', '', $order->recipient_phone),
'email' => $order->recipient_email,
],
// Данные для доставки до двери
'address' => $order->pickup_point_id ? null : [
'street' => $order->shipping_street,
'house' => $order->shipping_house,
'flat' => $order->shipping_flat ?? '',
'comment' => $order->shipping_comment ?? '',
],
// Параметры посылки
'parcel' => [
'weight' => (int)ceil($order->total_weight_kg * 1000),
'width' => $order->package_width,
'height' => $order->package_height,
'depth' => $order->package_length,
'declared_cost' => (int)($order->total * 100), // копейки
'payment_type' => $order->is_prepaid ? 'prepaid' : 'cod', // cod = наложенный платёж
'cod_amount' => $order->is_prepaid ? 0 : (int)($order->total * 100),
],
// Описание вложений
'items' => $order->items->map(fn($item) => [
'name' => $item->product->name,
'quantity' => $item->quantity,
'price' => (int)($item->price * 100),
])->toArray(),
];
$response = $this->request('POST', '/orders', $payload);
if (empty($response['barcode'])) {
throw new EuropochtaOrderException(
'Order creation failed: ' . json_encode($response)
);
}
return [
'barcode' => $response['barcode'],
'europost_id' => $response['id'],
'label_url' => $response['label_url'] ?? null,
];
}
Получение этикетки
public function getLabel(string $barcode): string
{
$response = Http::withToken($this->authenticate())
->get($this->baseUrl . '/labels/' . $barcode);
// Возвращает PDF
return $response->body();
}
// Пакетная печать этикеток
public function getBatchLabels(array $barcodes): string
{
$response = Http::withToken($this->authenticate())
->post($this->baseUrl . '/labels/batch', [
'barcodes' => $barcodes,
]);
return $response->body();
}
Отслеживание
public function trackParcel(string $barcode): array
{
$response = $this->request('GET', '/tracking/' . $barcode);
return [
'status' => $response['current_status'] ?? '',
'location' => $response['current_location'] ?? '',
'events' => collect($response['events'] ?? [])->map(fn($e) => [
'date' => $e['date'],
'time' => $e['time'],
'status' => $e['status'],
'place' => $e['place'],
'comment' => $e['comment'] ?? '',
])->toArray(),
];
}
Webhook уведомления
// Регистрация webhook
$this->request('POST', '/webhooks', [
'url' => 'https://yoursite.by/api/europost/webhook',
'events' => ['order.status_changed', 'order.delivered', 'order.returned'],
]);
// Обработчик
public function handleWebhook(Request $request): Response
{
// Проверка подписи
$signature = hash_hmac('sha256', $request->getContent(), config('services.europost.webhook_secret'));
if ($signature !== $request->header('X-Europost-Signature')) {
return response('Forbidden', 403);
}
$data = $request->json()->all();
$order = Order::where('europost_barcode', $data['barcode'])->first();
if ($order) {
$order->update(['shipping_status' => $data['status']]);
if ($data['status'] === 'delivered') {
dispatch(new MarkOrderDelivered($order));
}
}
return response('ok', 200);
}
Особенности белорусского рынка
НДС в Беларуси — 20%. При формировании документов для посылки с объявленной ценностью стоит указывать стоимость с НДС. Максимальный вес посылки Европочты — 30 кг. Наложенный платёж доступен для большинства точек выдачи.
Европочта активно развивает сеть постаматов — автоматических пунктов выдачи, которые работают 24/7. При отображении карты ПВЗ стоит визуально разделять обычные точки и постаматы.
Сроки
Базовая интеграция (расчёт + ПВЗ + создание заказов) — 4–5 рабочих дней. Добавление webhook и трекинга — ещё 2 дня.







