telvero-sales-backoffice/api/services/PostcodeService.php

209 lines
6.9 KiB
PHP

<?php
/**
* Postcode Service - Handles postcode lookup via postcode.tech API
* with fallback to openpostcode.nl API
*/
class PostcodeService
{
/**
* Lookup address by postcode and house number
* Uses postcode.tech as primary, falls back to openpostcode.nl
*
* @param string $postcode
* @param string $number
* @return array
*/
public static function lookup(string $postcode, string $number): array
{
$postcode = strtoupper(str_replace(' ', '', $postcode));
if (empty($postcode) || empty($number)) {
return [
'success' => false,
'error' => 'Postcode en huisnummer zijn verplicht',
'http_code' => 400
];
}
// Try primary service (postcode.tech)
$result = self::lookupPostcodeTech($postcode, $number);
// If primary service fails (server error or timeout), try fallback
if (!$result['success'] && $result['http_code'] >= 500) {
$result = self::lookupOpenPostcode($postcode, $number);
}
return $result;
}
/**
* Lookup via postcode.tech API (primary)
*
* @param string $postcode
* @param string $number
* @return array
*/
private static function lookupPostcodeTech(string $postcode, string $number): array
{
$url = "https://postcode.tech/api/v1/postcode?postcode={$postcode}&number={$number}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer " . $_ENV['POSTCODE_TECH_KEY']
]);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError || $httpCode >= 500) {
return [
'success' => false,
'error' => 'Postcode.tech service niet bereikbaar',
'details' => $curlError,
'http_code' => 503
];
}
if ($httpCode >= 400) {
return [
'success' => false,
'error' => self::translateErrorMessage($response, $httpCode),
'http_code' => $httpCode
];
}
$data = json_decode($response, true);
return [
'success' => true,
'data' => $data,
'http_code' => 200
];
}
/**
* Lookup via openpostcode.nl API (fallback)
* No authentication required
*
* @param string $postcode
* @param string $number
* @return array
*/
private static function lookupOpenPostcode(string $postcode, string $number): array
{
$url = "https://openpostcode.nl/api/address?postcode={$postcode}&huisnummer={$number}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError || $httpCode >= 500) {
return [
'success' => false,
'error' => 'Postcode service niet bereikbaar, vul straat en woonplaats zelf in',
'details' => $curlError,
'http_code' => 503
];
}
if ($httpCode >= 400) {
return [
'success' => false,
'error' => self::translateErrorMessage($response, $httpCode),
'http_code' => $httpCode
];
}
$data = json_decode($response, true);
// Transform openpostcode.nl response to match postcode.tech format
$transformedData = self::transformOpenPostcodeResponse($data);
return [
'success' => true,
'data' => $transformedData,
'http_code' => 200
];
}
/**
* Transform openpostcode.nl response to match postcode.tech format
*
* openpostcode.nl returns:
* - postcode, huisnummer, straat, buurt, wijk, woonplaats, gemeente, provincie, latitude, longitude
*
* postcode.tech returns:
* - postcode, number, street, city, municipality, province, geo (lat, lng)
*
* @param array $data
* @return array
*/
private static function transformOpenPostcodeResponse(array $data): array
{
return [
'postcode' => $data['postcode'] ?? '',
'number' => $data['huisnummer'] ?? '',
'street' => $data['straat'] ?? '',
'city' => $data['woonplaats'] ?? '',
'municipality' => $data['gemeente'] ?? '',
'province' => $data['provincie'] ?? '',
'geo' => [
'lat' => $data['latitude'] ?? null,
'lng' => $data['longitude'] ?? null
]
];
}
/**
* Translate API error messages to user-friendly Dutch messages
*
* @param string $response Raw API response
* @param int $httpCode HTTP status code
* @return string User-friendly error message in Dutch
*/
private static function translateErrorMessage(string $response, int $httpCode): string
{
$decoded = json_decode($response, true);
$apiError = ($decoded && isset($decoded['error'])) ? strtolower($decoded['error']) : '';
// Map common API error messages to Dutch user-friendly messages
if (strpos($apiError, 'huisnummer not found') !== false ||
strpos($apiError, 'house number not found') !== false ||
strpos($apiError, 'number not found') !== false) {
return 'Dit huisnummer is niet gevonden bij deze postcode. Controleer het huisnummer of vul het adres handmatig in.';
}
if (strpos($apiError, 'postcode not found') !== false ||
strpos($apiError, 'invalid postcode') !== false) {
return 'Deze postcode is niet gevonden. Controleer de postcode of vul het adres handmatig in.';
}
if (strpos($apiError, 'invalid') !== false) {
return 'Ongeldige invoer. Controleer de postcode en het huisnummer.';
}
if ($httpCode === 404) {
return 'Adres niet gevonden. Controleer de postcode en het huisnummer of vul het adres handmatig in.';
}
if ($httpCode === 429) {
return 'Te veel verzoeken. Probeer het later opnieuw of vul het adres handmatig in.';
}
// Default message
return 'Adres niet gevonden. Vul straat en woonplaats handmatig in.';
}
}