Europ Post Delivery Service Integration on Website
Europ Post is a Belarusian private delivery service specializing in internet store deliveries. Works primarily in Belarus, provides pickup points network, postamates, and courier delivery. For Belarusian e-commerce projects often becomes main option due to developed PVZ network and clear API.
Connecting to API
Europ Post provides REST API with login/password authorization. Documentation available after system registration. Basic client:
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() ?? [];
}
}
Delivery Cost Calculation
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();
}
Cities and Pickup Points List
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',
'cash_allowed'=> (bool)($p['cash'] ?? false),
])
->toArray();
}
Order Creation
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_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,
];
}
Label Retrieval
public function getLabel(string $barcode): string
{
$response = Http::withToken($this->authenticate())
->get($this->baseUrl . '/labels/' . $barcode);
return $response->body();
}
public function getBatchLabels(array $barcodes): string
{
$response = Http::withToken($this->authenticate())
->post($this->baseUrl . '/labels/batch', [
'barcodes' => $barcodes,
]);
return $response->body();
}
Tracking
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 Notifications
// Register webhook
$this->request('POST', '/webhooks', [
'url' => 'https://yoursite.by/api/europost/webhook',
'events' => ['order.status_changed', 'order.delivered', 'order.returned'],
]);
// Handler
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);
}
Belarusian Market Features
VAT in Belarus is 20%. When forming shipment documents with declared value, include VAT. Max Europ Post parcel weight is 30 kg. COD available at most pickup points.
Europ Post actively develops postamate network — 24/7 automatic pickup points. When displaying PVZ map, visually distinguish regular points from postamates.
Timeline
Basic integration (calculation + PVZ + order creation) — 4–5 working days. Add webhook and tracking — another 2 days.







