int|null, 'message' => string) albo null. */ private $error = null; public function __construct($userid, $serviceid, $timeout = 10) { $this->userid = (int) $userid; $this->serviceid = (int) $serviceid; $this->timeout = (int) $timeout; } /** * Wariant jednocenowy — produkty w jednej cenie. * Wymaga numeru, na który kupujący wysłał wiadomość. * * @return array|false dane kodu albo false, gdy kod jest nieważny lub wystąpił błąd */ public function verify($code, $number) { return $this->call(self::URL_SINGLE, array( 'code' => $code, 'number' => $number, )); } /** * Wariant wielocenowy — produkty w różnych cenach. * Numer nie jest potrzebny, wraca w odpowiedzi. * * @return array|false */ public function verifyMulti($code) { return $this->call(self::URL_MULTI, array('code' => $code)); } /** * Błąd ostatniego wywołania. Rozróżnia sytuacje: * - kod nieważny -> verify() zwraca false, getError() zwraca null * - błąd wywołania -> verify() zwraca false, getError() zwraca powód */ public function getError() { return $this->error; } /** * Powiadomienie wysyłane na adres skryptu z panelu (metoda POST). * Zwraca uporządkowaną tablicę albo false, gdy żądanie nie jest naszym powiadomieniem. * * Uwaga: wartości docierają zakodowane, dlatego przepuszczamy je przez urldecode(). * * @param array|null $post domyślnie $_POST */ public static function readNotification($post = null) { $post = $post === null ? $_POST : $post; if (!isset($post['messageID'], $post['code'], $post['serviceID'])) { return false; } $get = function ($key) use ($post) { return isset($post[$key]) ? urldecode((string) $post[$key]) : ''; }; return array( 'test' => (isset($post['test']) && ($post['test'] === 'true' || $post['test'] === '1')), 'userid' => (int) $get('userID'), 'serviceid' => (int) $get('serviceID'), 'messageid' => $get('messageID'), 'code' => $get('code'), 'reply' => $get('reply'), // treść wysłana do kupującego 'text' => $get('txt'), // treść, którą wysłał kupujący 'phone' => $get('numberUser'), // numer kupującego 'number' => $get('numberSMS'), // numer, na który wysłano SMS 'date' => $get('date'), ); } /** Odpowiedź, której oczekujemy na powiadomienie. */ public static function confirmNotification() { header('Content-Type: text/plain'); echo 'OK'; } /* ------------------------------------------------------------------ */ private function call($url, array $params) { $this->error = null; $code = strtoupper(trim((string) $params['code'])); if (strlen($code) !== 8) { $this->error = array('code' => self::ERR_CODE, 'message' => 'Kod musi mieć 8 znaków.'); return false; } $params['code'] = $code; $params['userid'] = $this->userid; $params['serviceid'] = $this->serviceid; $raw = $this->request($url . '?' . http_build_query($params)); if ($raw === false) { $this->error = array('code' => null, 'message' => 'Brak połączenia z API.'); return false; } $data = json_decode($raw, true); if (!is_array($data)) { $this->error = array('code' => null, 'message' => 'Odpowiedź nie jest poprawnym JSON-em.'); return false; } /* API zwraca błędy w dwóch układach — obsługujemy oba. */ if (isset($data['error'])) { $this->error = array( 'code' => isset($data['error']['errorCode']) ? (int) $data['error']['errorCode'] : null, 'message' => isset($data['error']['message']) ? $data['error']['message'] : 'Błąd API.', ); return false; } if (isset($data['connect']) && $data['connect'] === false) { $this->error = array( 'code' => isset($data['data']['errorCode']) ? (int) $data['data']['errorCode'] : null, 'message' => isset($data['data']['message']) ? $data['data']['message'] : 'Błąd API.', ); return false; } $body = isset($data['data']) && is_array($data['data']) ? $data['data'] : array(); /* status = 1 oznacza kod ważny; każde takie sprawdzenie zużywa jedno użycie. */ if (!isset($body['status']) || (int) $body['status'] !== 1) { return false; } return array( 'code' => $code, 'service' => isset($body['service']) ? (int) $body['service'] : 0, 'number' => isset($body['number']) ? (string) $body['number'] : '', 'phone' => isset($body['phone']) ? (string) $body['phone'] : '', 'reply' => isset($body['reply']) ? (string) $body['reply'] : '', 'usesLeft' => isset($body['used']) ? (int) $body['used'] : null, ); } private function request($url) { if (function_exists('curl_init')) { $ch = curl_init($url); curl_setopt_array($ch, array( CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $this->timeout, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 3, )); $raw = curl_exec($ch); curl_close($ch); return $raw; } $context = stream_context_create(array('http' => array('timeout' => $this->timeout))); return @file_get_contents($url, false, $context); } } /* ====================================================================== */ /* PRZYKŁADY UŻYCIA */ /* */ /* Poniższy blok wykonuje się tylko przy bezpośrednim otwarciu pliku. */ /* Po wgraniu klasy na serwer możesz go usunąć. */ /* ====================================================================== */ if (basename(__FILE__) !== basename($_SERVER['SCRIPT_FILENAME'])) { return; } $USERID = 1234; // podmień na swój $SERVICEID = 610; // podmień na swój $sms = new PayByLinkSms($USERID, $SERVICEID); /* --- 1. Wariant jednocenowy ------------------------------------------- */ $result = $sms->verify('ABCD1234', '7168'); if ($result) { // Kod poprawny. Zapisz to u siebie i odblokuj zamówienie. // Nie sprawdzaj tego samego kodu drugi raz — każde sprawdzenie zużywa jedno użycie. echo 'Kod przyjęty, numer kupującego: ' . $result['phone'] . "\n"; } elseif ($sms->getError()) { // Coś nie zadziałało po stronie wywołania: zły serviceid, brak połączenia, zły numer. echo 'Błąd: ' . $sms->getError()['message'] . "\n"; } else { // Wywołanie się udało, ale kod jest nieważny albo już wykorzystany. echo "Kod nieaktywny.\n"; } /* --- 2. Wariant wielocenowy ------------------------------------------- */ $result = $sms->verifyMulti('ABCD1234'); if ($result) { echo 'Kod przyjęty. Wysłany na numer: ' . $result['number'] . "\n"; echo 'Pozostało użyć: ' . $result['usesLeft'] . "\n"; } /* --- 3. Rozróżnianie przyczyn błędu ----------------------------------- */ if (!$sms->verifyMulti('ZZZZ0000')) { $error = $sms->getError(); if ($error === null) { echo "Kod nie istnieje lub został wykorzystany.\n"; } else { switch ($error['code']) { case PayByLinkSms::ERR_CODE: echo "Kod ma nieprawidłową długość.\n"; break; case PayByLinkSms::ERR_SERVICE: echo "Nieprawidłowy userid lub serviceid.\n"; break; case PayByLinkSms::ERR_NUMBER: echo "Numer nie należy do tej usługi.\n"; break; default: echo 'Błąd wywołania: ' . $error['message'] . "\n"; } } } /* --- 4. Odbiór powiadomienia ------------------------------------------ */ /* Wgraj to jako osobny plik i podaj jego adres w panelu usługi. Wywołamy go metodą POST zaraz po odebraniu wiadomości od kupującego. require_once 'PayByLinkSms.php'; $notify = PayByLinkSms::readNotification(); if ($notify === false) { http_response_code(400); exit; } if ($notify['test']) { // Wiadomość testowa wysłana z panelu — nie realizuj zamówienia. PayByLinkSms::confirmNotification(); exit; } // Zapisz kod u siebie i powiąż go z zamówieniem. // $notify['code'], $notify['phone'], $notify['number'], $notify['messageid'] PayByLinkSms::confirmNotification(); */