Przykłady kodu (wzorce)
Poniżej masz gotowe wzorce zgodne z IVR API: bezpieczne parsowanie webhooka, odpowiedź Action/Session,
prosty state machine pod DTMF oraz fallback. Kod jest „do adaptacji” — w produkcji dołóż monitoring,
limity i zasady logowania danych wrażliwych.
Wzorzec 1: minimalny endpoint + poprawne parsowanie request (PHP 5.3)
<?php
header('Content-Type: application/json');
// 1) Wczytaj payload — najpierw raw JSON, potem fallback do $_POST['request'].
$raw = file_get_contents('php://input');
$data = null;
if ($raw !== false && trim($raw) !== '') {
$data = json_decode($raw, true);
}
if (!$data && isset($_POST['request'])) {
// Niektóre integracje wysyłają request jako pole formularza.
if (get_magic_quotes_gpc()) {
$_POST['request'] = stripslashes($_POST['request']);
}
$data = json_decode($_POST['request'], true);
}
// 2) Ujednolić: czasem dostajesz { "request": {...} }, a czasem sam obiekt.
if (!is_array($data)) { exit; }
$request = isset($data['request']) && is_array($data['request']) ? $data['request'] : $data;
// 3) Pobierz podstawy do logów/diagnostyki.
$uniqueCallId = isset($request['UniqueCallId']) ? $request['UniqueCallId'] : '';
$eventName = '';
if (isset($request['Event']) && is_array($request['Event'])) {
$eventName = isset($request['Event']['EventName']) ? $request['Event']['EventName'] : '';
}
// TODO: logowanie po Twojej stronie (UniqueCallId + EventName + czas odpowiedzi)
// 4) Minimalna odpowiedź (np. na start testów).
$response = array(
'Action' => array('Type' => 'Play', 'Prompt' => '1'),
'Session' => array('step' => 'welcome')
);
echo json_encode($response);
?>
Ten wzorzec eliminuje klasyczny problem: „na serwerze nic nie przychodzi”, gdy payload jest wysyłany jako raw JSON,
a nie jako $_POST.
Wzorzec 2: weryfikacja Hash (sekret) + bezpieczne porównanie (PHP 5.3)
<?php
header('Content-Type: application/json');
// Timing-safe compare dla PHP 5.3 (hash_equals jest od PHP 5.6).
function timingSafeEquals($a, $b) {
$a = (string)$a;
$b = (string)$b;
if (strlen($a) !== strlen($b)) return false;
$res = 0;
for ($i = 0; $i < strlen($a); $i++) {
$res |= ord($a[$i]) ^ ord($b[$i]);
}
return $res === 0;
}
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (!is_array($data)) { exit; }
$request = isset($data['request']) && is_array($data['request']) ? $data['request'] : $data;
// Oczekiwany sekret (Hash) przechowuj poza repo (config/ENV/secret manager).
$expectedHash = 'TU_WSTAW_SWOJ_HASH';
$incomingHash = isset($request['Hash']) ? $request['Hash'] : '';
if ($incomingHash === '' || !timingSafeEquals($incomingHash, $expectedHash)) {
// Nie zdradzaj szczegółów w odpowiedzi. Loguj incydent u siebie.
echo json_encode(array(
'Action' => array('Type' => 'Hangup'),
'Session' => array('reason' => 'unauthorized')
));
exit;
}
// ... dalej normalna logika (Action/Session)
echo json_encode(array(
'Action' => array('Type' => 'Play', 'Prompt' => '1'),
'Session' => array('step' => 'authorized')
));
?>
Hash traktuj jak sekret: nie loguj go wprost, rotuj w razie podejrzenia wycieku i trzymaj poza kodem aplikacji.
Wzorzec 3: state machine (ID + PIN) + fallback do kolejki (PHP 5.3)
<?php
header('Content-Type: application/json');
// --- Parsowanie requestu (jak w wzorcu 1) ---
$raw = file_get_contents('php://input');
$data = null;
if ($raw !== false && trim($raw) !== '') {
$data = json_decode($raw, true);
}
if (!$data && isset($_POST['request'])) {
if (get_magic_quotes_gpc()) { $_POST['request'] = stripslashes($_POST['request']); }
$data = json_decode($_POST['request'], true);
}
if (!is_array($data)) { exit; }
$request = isset($data['request']) && is_array($data['request']) ? $data['request'] : $data;
// --- Kontekst zdarzenia ---
$event = isset($request['Event']) && is_array($request['Event']) ? $request['Event'] : array();
$eventName = isset($event['EventName']) ? $event['EventName'] : '';
$eventData = isset($event['EventData']) ? $event['EventData'] : '';
// --- Session (stan rozmowy) ---
$session = isset($request['Session']) && is_array($request['Session']) ? $request['Session'] : array();
if (!isset($session['state'])) $session['state'] = 'ASK_ID';
if (!isset($session['tries'])) $session['tries'] = 0;
$response = array();
// Helper: przejście do fallbacku
function setFallback(&$response, &$session) {
// Destination poniżej to przykład — wstaw login obiektu/kolejki z Twojej konfiguracji.
$response['Action'] = array(
'Type' => 'CallByObjectLogin',
'Destination' => 'kolejka_ogolna',
'Options' => 'Timeout=30'
);
$session['state'] = 'END';
}
if ($session['state'] === 'ASK_ID') {
// Pytamy o ID (np. zamówienia / zgłoszenia)
$response['Action'] = array('Type' => 'GetDTMF', 'Prompt' => '1', 'Timeout' => '1200', 'MaxDigits' => '10');
$session['state'] = 'WAIT_ID';
}
else if ($session['state'] === 'WAIT_ID') {
if ($eventName === 'GetDTMF' && $eventData !== '' && $eventData !== 'timeout') {
$session['id'] = $eventData;
$session['state'] = 'ASK_PIN';
} else {
$session['tries']++;
if ($session['tries'] >= 3) {
setFallback($response, $session);
} else {
$session['state'] = 'ASK_ID';
$response['Action'] = array('Type' => 'Play', 'Prompt' => '3'); // np. "Spróbuj ponownie"
}
}
if (!isset($response['Action'])) {
$response['Action'] = array('Type' => 'Wait', 'WaitTime' => '0');
}
}
else if ($session['state'] === 'ASK_PIN') {
$response['Action'] = array('Type' => 'GetDTMF', 'Prompt' => '2', 'Timeout' => '1200', 'MaxDigits' => '10');
$session['state'] = 'WAIT_PIN';
}
else if ($session['state'] === 'WAIT_PIN') {
if ($eventName === 'GetDTMF' && $eventData !== '' && $eventData !== 'timeout') {
$session['pin'] = $eventData;
// TODO: weryfikacja w bazie / CRM / ERP (tu tylko przykład)
$ok = (isset($session['id']) && $session['id'] === $session['pin']);
if ($ok) {
// Routing do obiektu po loginie (np. opiekun / dział) — przykładowa nazwa.
$response['Action'] = array(
'Type' => 'CallByObjectLogin',
'Destination' => 'obiekt001',
'Options' => 'PlayCallStatus,Timeout=30'
);
$session['state'] = 'END';
} else {
$session['tries']++;
if ($session['tries'] >= 3) {
setFallback($response, $session);
} else {
$session['state'] = 'ASK_ID';
$response['Action'] = array('Type' => 'Play', 'Prompt' => '3'); // np. "Błędne dane"
}
}
} else {
$session['tries']++;
if ($session['tries'] >= 3) {
setFallback($response, $session);
} else {
$session['state'] = 'ASK_PIN';
$response['Action'] = array('Type' => 'Wait', 'WaitTime' => '0');
}
}
}
else {
$response['Action'] = array('Type' => 'Hangup');
}
$response['Session'] = $session;
echo json_encode($response);
?>
Produkcyjnie dołóż: weryfikację Hash, maskowanie numerów w logach, monitoring czasu odpowiedzi webhooka i jasny fallback (żadnych pętli bez końca).
Uruchom u siebie
Załóż konto i przejdź szybki start — najpierw “Connected → Play”, potem DTMF, dopiero na końcu routing po danych.