Files
wp-postcode-plugin/session_dialog.php
2026-06-22 09:34:14 +02:00

488 lines
16 KiB
PHP

<?php
function init_postcode_handlers($uri)
{
if (strpos($uri, "/winkel/") !== false) {
add_action("wp_enqueue_scripts", "modal_styles");
add_action("wp_footer", "send_postcode_data");
if (!verify_pppr_cookie_string()) {
if (!is_admin()) {
WC()->cart->empty_cart();
}
add_action("wp_footer", "show_modal");
render_dialog_html();
}
}
add_action("wp_ajax_unset_my_session", "handle_unset_session_fetch");
add_action("wp_ajax_nopriv_unset_my_session", "handle_unset_session_fetch");
add_action("wp_footer", "modify_checkout_with_js");
add_action("wp_enqueue_scripts", "load_assets_reset_postcode_on_checkout");
}
function modal_styles()
{
wp_enqueue_style(
"prijs-per-postcode",
plugins_url("/assets/postcode_modal.css", __FILE__),
[], // Voeg een lege dependencies array toe (verplicht)
filemtime(plugin_dir_path(__FILE__) . "assets/postcode_modal.css"), // Voeg versie toe
);
}
function show_modal()
{
echo <<<'HTML'
<script id="postcode_modal_open">
const postcodeModal = document.querySelector("#postcode_modal");
postcodeModal.showModal();
</script>
HTML;
}
function send_postcode_data()
{
$URL = get_rest_url(null, "postcode-modal/v1/submit");
$nonce = wp_create_nonce("wp_rest");
echo <<<HTML
<script type="module">
const postcodeModal = document.querySelector("#postcode_modal");
const modalForm = document.querySelector("#postcode_modal_form");
const sndBtn = document.querySelector("#postcode_modal_submit");
const sndBtnLdr = sndBtn.querySelector(".btn_loader");
const sndBtnTxt = sndBtn.querySelector(".btn_text");
const errorMsg = document.querySelector("#error_message_modal_postcode");
// 1. Validatie bij blur (client-side checks)
modalForm.querySelectorAll("input").forEach(input => {
input.addEventListener("blur", ev => {
errorMsg.innerHTML = "";
if (!ev.target.validity.valid && ev.target.value !== "") {
errorMsg.innerHTML = ev.target.title;
}
});
});
// 2. Submit Handler
modalForm.addEventListener('submit', async (e) => {
e.preventDefault();
errorMsg.innerHTML = "";
// UI: Laadstatus
sndBtn.disabled = true;
sndBtnLdr.style.opacity = "1";
sndBtnTxt.style.opacity = "0";
const formData = new FormData(e.target);
const payload = Object.fromEntries(formData.entries());
try {
const resp = await fetch('{$URL}', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': '{$nonce}'
},
body: JSON.stringify(payload)
});
// Parse altijd eerst de JSON, zelfs bij HTTP fouten
const data = await resp.json();
// UI: Reset knop
sndBtn.disabled = false;
sndBtnLdr.style.opacity = "0";
sndBtnTxt.style.opacity = "1";
if (data.status === "error") {
const errorCode = data.code; // Komt nu correct door vanuit PHP
const apiMessage = data.message;
let userErrorMessage = "Gegevens zijn niet correct.";
// Specifieke foutafhandeling
if (errorCode === "HUISNUMMER_NOT_FOUND" || apiMessage.includes("not found")) {
userErrorMessage = "Adres is niet gevonden.";
}
else if (errorCode === "HUISNUMMER_AMBIGUOUS") {
const suggestions = data.suggestions || [];
if (suggestions.length > 0) {
userErrorMessage = `Meerdere opties: \${suggestions.join(", ")}. Voeg een toevoeging toe.`;
} else {
userErrorMessage = "Huisnummertoevoeging is verplicht.";
}
}
else if (errorCode === "CURL_ERROR" || errorCode === "JSON_ERROR") {
userErrorMessage = "Serverfout. Probeer het later opnieuw.";
}
errorMsg.innerHTML = userErrorMessage;
}
if (data.status === "success") {
// Succes: Modal sluiten en eventueel reload of update
postcodeModal.close();
// Optioneel: location.reload(); of update de pagina content
}
} catch (err) {
sndBtn.disabled = false;
sndBtnLdr.style.opacity = "0";
sndBtnTxt.style.opacity = "1";
errorMsg.innerHTML = "Er ging iets mis bij het verbinden.";
}
});
</script>
HTML;
}
function render_dialog_html()
{
echo <<<'HTML'
<dialog id="postcode_modal" class="postcode_modal" closedby="none">
<h2>Vul je postcode en huisnummer in.</h2>
<p>Onze prijzen zijn afhankelijk van de regio. Vul daarom de postcode en het huisnummer in om de exacte prijzen te bekijken.</p>
<form id="postcode_modal_form" method="post" action="" novalidate>
<div class="form_fields">
<!-- Dutch Postcode Field -->
<input
type="text"
name="postcode"
title="Voer een geldige postcode in (bijv. 1234AB of 1234 AB)."
pattern="[1-9][0-9]{3} ?(?!sa|sd|ss)[a-zA-Z]{2}"
placeholder="5010 AA"
size="8"
required
autocomplete="off"
/>
<!-- House Number Field -->
<input
type="text"
name="huisnummer"
title="Voer een geldig huisnummer in (bijv. 1, 1A, 1-A)."
pattern="\d+([\- ]?[a-zA-Z]+)?"
placeholder="10"
size="3"
required
autocomplete="off"
/>
<button id="postcode_modal_submit" type="submit"><span class="btn_text">OK</span>
<div class="btn_loader">
<svg viewBox="0 0 128 30" xmlns="http://www.w3.org/2000/svg">
<circle style="--i:0" cx="14" cy="15" r="14" />
<circle style="--i:1" cx="64" cy="15" r="14" />
<circle style="--i:2" cx="114" cy="15" r="14" />
</svg>
</div>
</button>
</div>
</form>
<div class="error_message_modal_postcode" id="error_message_modal_postcode" aria-live="polite"></div>
</dialog>
HTML;
}
function handle_postcode_modal($data)
{
$params = $data->get_params();
$nonce = $data->get_header("X-WP-Nonce");
// 1. Security
if (!wp_verify_nonce($nonce, "wp_rest")) {
echo json_encode(["status" => "error", "message" => "nononce"]);
exit();
}
// 2. Basis Validatie
if (!verify_postcode($params["postcode"])) {
echo json_encode(["status" => "error", "message" => "postcode"]);
exit();
}
if (!verify_huisnummer($params["huisnummer"])) {
echo json_encode(["status" => "error", "message" => "huisnummer"]);
exit();
}
// 3. API Call (Ruwe data)
$result = getStraatnaam($params["postcode"], $params["huisnummer"]);
// 4. Validatie & Logic
$hasError = isset($result["error"]);
$errorCode = null;
$errorMessage = null;
$candidates = [];
$straatnaam = null;
$woonplaats = null;
if ($hasError) {
$errorCode = $result["error"]["code"] ?? "UNKNOWN_ERROR";
$errorMessage = $result["error"]["message"] ?? "Onbekende fout";
$candidates = $result["error"]["details"]["candidates"] ?? [];
// SPECIAL CASE: HUISNUMMER_AMBIGUOUS
if ($errorCode === "HUISNUMMER_AMBIGUOUS" && !empty($candidates)) {
$plainMatch = null;
// Zoek naar candidate ZONDER toevoeging
foreach ($candidates as $candidate) {
if (
empty($candidate["huisletter"]) &&
empty($candidate["huisnummertoevoeging"])
) {
$plainMatch = $candidate;
break;
}
}
// Als we een 'platte' match hebben (bijv. 15), dan is het SUCCES
if ($plainMatch) {
$hasError = false; // Negeer de error
$straatnaam = $plainMatch["straat"];
$woonplaats = $plainMatch["woonplaats"];
}
}
} else {
// Normaal succes (geen error in response)
if (!empty($result["results"])) {
$straatnaam = $result["results"][0]["straat"];
$woonplaats = $result["results"][0]["woonplaats"];
} else {
// Edge case: geen error veld, maar ook geen results
$hasError = true;
$errorCode = "NO_RESULTS";
$errorMessage = "Adres niet gevonden";
}
}
// 5. Response sturen
if ($hasError) {
echo json_encode([
"status" => "error",
"code" => $errorCode,
"message" => $errorMessage,
"suggestions" => $result["error"]["details"]["suggestions"] ?? [],
"apirequest" => "openpostcode.nl",
]);
exit();
}
set_pppr_cookie(
postcode_in_range($params["postcode"]),
$straatnaam,
$params["huisnummer"],
$params["postcode"],
$woonplaats
);
echo json_encode([
"status" => "success",
"message" => "all good",
]);
exit();
}
function register_modal_api()
{
register_rest_route("postcode-modal/v1", "submit", [
"methods" => "POST",
"callback" => "handle_postcode_modal",
]);
}
function verify_postcode($postcode)
{
if (!preg_match('/^[0-9]{4}\s?[A-Za-z]{2}$/', $postcode) === 1) {
return false;
}
return true;
}
function verify_huisnummer($huisnummer)
{
if (!preg_match('/^[0-9]{4}\s?[A-Za-z]{2}$/', $huisnummer) === 1) {
return false;
}
return true;
}
function getStraatnaam($postcode, $huisnummer)
{
$url =
"https://openpostcode.nl/api/v2/address?postcode=" .
urlencode($postcode) .
"&huisnummer=" .
urlencode($huisnummer);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_USERAGENT, "PHP/OpenPostcodeClient");
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_error($ch)) {
curl_close($ch);
return [
"error" => ["code" => "CURL_ERROR", "message" => curl_error($ch)],
];
}
curl_close($ch);
// Decodeer als associatieve array (true)
$data = json_decode($response, true);
// Als JSON decode faalt
if (json_last_error() !== JSON_ERROR_NONE) {
return [
"error" => [
"code" => "JSON_ERROR",
"message" => "Invalid JSON response",
],
];
}
// Geef de RUWE response terug (inclusief meta, error, results, etc.)
return $data;
}
function postcode_in_range($postcode)
{
$vals = get_option("local_postcodes_values", "");
$rows = preg_split("/\R/", $vals, -1, PREG_SPLIT_NO_EMPTY);
$pc_arr = [];
foreach ($rows as $row) {
$row = trim($row);
$postcode_range = explode("-", $row);
$pc_arr[] = [(int) $postcode_range[0], (int) $postcode_range[1]];
}
$cleanPostcode = strtoupper(preg_replace("/\s+/", "", $postcode));
if (!preg_match('/^\d{4}[A-Z]{2}$/', $cleanPostcode)) {
return false;
}
$numberPart = (int) substr($cleanPostcode, 0, 4);
foreach ($pc_arr as $pc_to_check) {
if ($numberPart >= $pc_to_check[0] && $numberPart <= $pc_to_check[1]) {
return true;
}
}
return false;
}
function modify_checkout_with_js()
{
if (
!is_checkout() ||
(is_wc_endpoint_url() && !is_wc_endpoint_url("order-received"))
) {
return;
}
$data = get_PPPR_data();
$city = $data["city"];
$postcode = $formatted_postcode = preg_replace(
"/(\d+)([A-Z]+)/",
'$1 $2',
strtoupper($data["postcode"]),
);
$address =
$data["street"] . " " . strtoupper($data["house_number"]);
echo <<<HTML
<script type="text/javascript" id="fill_address_fields">
jQuery(document).ready(function(\$) {
fillCheckoutFields();
\$(document.body).on('updated_checkout', fillCheckoutFields);
});
function fillCheckoutFields() {
if (typeof wp !== 'undefined' && wp.data && wp.data.dispatch) {
const store = 'wc/store/cart';
wp.data.dispatch(store).setShippingAddress({
first_name: '',
last_name: '',
address_1: '{$address}',
address_2: '',
city: '{$city}',
state: '',
postcode: '{$postcode}',
country: 'NL',
phone: '',
email: ''
});
//make fields READONLY and ppstcode reset.
setTimeout(() => {
// make prefilled fiields readonly.
\$('#shipping-postcode, #shipping-city, #shipping-address_1')
.prop('readonly', true)
.css('background', '#f9f9f9');
// create postcode reset button
const div = document.createElement("div");
div.setAttribute("class", "postcode-reset")
div.innerHTML = `
<a href="#" class="reset-postcode-show-comfirm" >Reset postcode.</a>
<span class="bevestiging"> Deze handeling leegt ook de winkelwagen. Weet je het zeker?
<a href="#" class="accept">&nbsp;&nbsp;JA&nbsp;</a>/<a href="#" class="decline">&nbsp;nee&nbsp;&nbsp;</a>
</span> `;
div.style.width = "100%";
document.querySelector(".wc-block-components-address-form__city").after(div);
}, 500);
jQuery(document.body).trigger('update_checkout');
} else {
console.error('WooCommerce Blocks API is niet beschikbaar');
}
}
</script>
HTML;
}
function load_assets_reset_postcode_on_checkout()
{
if (is_checkout() && !is_wc_endpoint_url()) {
wp_enqueue_style(
"reset-postcode-style",
plugin_dir_url(__FILE__) . "assets/reset-postcode.css",
[],
"1.0.0",
);
wp_enqueue_script(
"reset-postcode-script",
plugin_dir_url(__FILE__) . "assets/reset-postcode.js",
[],
"1.0.0",
true,
);
// Pass PHP variables to JavaScript
wp_localize_script("reset-postcode-script", "ajax_object", [
"ajax_url" => admin_url("admin-ajax.php"),
"nonce" => wp_create_nonce("reset_postcode_nonce"), // Creates a secure token
]);
}
}
function handle_unset_session_fetch()
{
// Verify the nonce for security
if (!wp_verify_nonce($_POST["nonce"], "reset_postcode_nonce")) {
wp_die("Security check failed.");
}
unset_pppr_cookie();
// Send a JSON response
wp_send_json_success();
}