V1
This commit is contained in:
parent
de4a531553
commit
7a4a45e227
197
api.php
197
api.php
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TELVERO BACKOFFICE - API PROXY (V6.9 - SERVER-SAFE ATTRIBUTION)
|
* TELVERO BACKOFFICE - API PROXY (V7.0 FINAL - WITH 100 EURO THRESHOLD)
|
||||||
*/
|
*/
|
||||||
session_start();
|
session_start();
|
||||||
ini_set('display_errors', 0);
|
ini_set('display_errors', 0);
|
||||||
@ -18,108 +19,62 @@ use Mollie\Api\MollieApiClient;
|
|||||||
|
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
// --- DATABASE CONNECTIE ---
|
||||||
$db = new mysqli($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASS'], $_ENV['DB_NAME']);
|
$db = new mysqli($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASS'], $_ENV['DB_NAME']);
|
||||||
|
if ($db->connect_error) {
|
||||||
|
die(json_encode(['error' => 'Database connectie mislukt']));
|
||||||
|
}
|
||||||
|
|
||||||
$action = $_GET['action'] ?? '';
|
$action = $_GET['action'] ?? '';
|
||||||
|
|
||||||
// --- AUTH ---
|
// --- 1. LOGIN ACTIE ---
|
||||||
if ($action === 'login') {
|
if ($action === 'login') {
|
||||||
$input = json_decode(file_get_contents('php://input'), true);
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
$stmt = $db->prepare("SELECT password, full_name FROM sales_users WHERE username = ?");
|
$stmt = $db->prepare("SELECT password, full_name FROM sales_users WHERE username = ?");
|
||||||
$stmt->bind_param("s", $input['username']);
|
$stmt->bind_param("s", $input['username']);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$res = $stmt->get_result()->fetch_assoc();
|
$res = $stmt->get_result()->fetch_assoc();
|
||||||
|
|
||||||
if ($res && password_verify($input['password'], $res['password'])) {
|
if ($res && password_verify($input['password'], $res['password'])) {
|
||||||
$_SESSION['user'] = $input['username'];
|
$_SESSION['user'] = $input['username'];
|
||||||
$_SESSION['full_name'] = $res['full_name'];
|
$_SESSION['full_name'] = $res['full_name'];
|
||||||
session_write_close();
|
session_write_close();
|
||||||
echo json_encode(['success' => true, 'user' => $res['full_name']]);
|
echo json_encode(['success' => true, 'user' => $res['full_name']]);
|
||||||
} else {
|
} else {
|
||||||
http_response_code(401); echo json_encode(['error' => 'Login mislukt']);
|
http_response_code(401);
|
||||||
|
echo json_encode(['error' => 'Inloggegevens onjuist']);
|
||||||
}
|
}
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($_SESSION['user'])) { http_response_code(403); exit; }
|
// Auth check voor alle andere acties
|
||||||
|
if (!isset($_SESSION['user']) && $action !== 'login') {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['error' => 'Sessie verlopen']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CLIENTS ---
|
||||||
$woocommerce = new Client($_ENV['WC_URL'], $_ENV['WC_KEY'], $_ENV['WC_SECRET'], ['version' => 'wc/v3', 'verify_ssl' => false, 'timeout' => 400]);
|
$woocommerce = new Client($_ENV['WC_URL'], $_ENV['WC_KEY'], $_ENV['WC_SECRET'], ['version' => 'wc/v3', 'verify_ssl' => false, 'timeout' => 400]);
|
||||||
|
|
||||||
// --- CREATE ORDER ---
|
// --- 2. HELPERS ---
|
||||||
if ($action === 'create_order') {
|
|
||||||
$input = json_decode(file_get_contents('php://input'), true);
|
|
||||||
try {
|
|
||||||
$mediacode = $input['mediacode_internal'] ?? 'Geen';
|
|
||||||
$wc_gateway_id = $input['payment_method'];
|
|
||||||
$mollie_method = str_replace(['mollie_wc_gateway_', 'rve_'], '', $wc_gateway_id);
|
|
||||||
|
|
||||||
$input['payment_method'] = $wc_gateway_id;
|
|
||||||
|
|
||||||
// Compacte meta_data om WAF blokkades te voorkomen
|
|
||||||
$input['meta_data'] = [
|
|
||||||
['key' => '_wc_order_attribution_source_type', 'value' => 'utm'],
|
|
||||||
['key' => '_wc_order_attribution_utm_source', 'value' => 'SalesPanel'],
|
|
||||||
['key' => '_wc_order_attribution_utm_campaign', 'value' => $mediacode],
|
|
||||||
['key' => 'Mediacode', 'value' => $mediacode]
|
|
||||||
];
|
|
||||||
|
|
||||||
// 1. Order aanmaken (Hier gaat de 500 error vaak mis door WAF)
|
|
||||||
$order = $woocommerce->post('orders', $input);
|
|
||||||
|
|
||||||
$mollie = new MollieApiClient();
|
|
||||||
$mollie->setApiKey($_ENV['MOLLIE_KEY']);
|
|
||||||
|
|
||||||
$paymentData = [
|
|
||||||
"amount" => ["currency" => "EUR", "value" => number_format((float)$order->total, 2, '.', '')],
|
|
||||||
"description" => "Order #{$order->id}",
|
|
||||||
"redirectUrl" => $_ENV['WC_URL'] . "/checkout/order-received/{$order->id}/?key={$order->order_key}&order_id={$order->id}&utm_source=SalesPanel&utm_campaign={$mediacode}",
|
|
||||||
"webhookUrl" => $_ENV['WC_URL'] . "/wc-api/{$wc_gateway_id}/?key={$order->order_key}&order_id={$order->id}",
|
|
||||||
"method" => $mollie_method,
|
|
||||||
"metadata" => ["order_id" => (string)$order->id, "mediacode" => $mediacode]
|
|
||||||
];
|
|
||||||
|
|
||||||
// Verplichte adresvelden voor achteraf betalen
|
|
||||||
if (in_array($mollie_method, ['in3', 'klarna', 'klarnapaylater', 'klarnasliceit', 'riverty'])) {
|
|
||||||
$paymentData["billingAddress"] = [
|
|
||||||
"givenName" => $input['billing']['first_name'],
|
|
||||||
"familyName" => $input['billing']['last_name'],
|
|
||||||
"email" => $input['billing']['email'],
|
|
||||||
"streetAndNumber" => $input['billing']['address_1'],
|
|
||||||
"city" => $input['billing']['city'],
|
|
||||||
"postalCode" => $input['billing']['postcode'],
|
|
||||||
"country" => "NL"
|
|
||||||
];
|
|
||||||
$paymentData["lines"] = [[
|
|
||||||
"name" => "Bestelling #" . $order->id,
|
|
||||||
"quantity" => 1,
|
|
||||||
"unitPrice" => ["currency" => "EUR", "value" => number_format((float)$order->total, 2, '.', '')],
|
|
||||||
"totalAmount" => ["currency" => "EUR", "value" => number_format((float)$order->total, 2, '.', '')],
|
|
||||||
"vatRate" => "21.00",
|
|
||||||
"vatAmount" => ["currency" => "EUR", "value" => number_format((float)$order->total_tax, 2, '.', '')]
|
|
||||||
]];
|
|
||||||
}
|
|
||||||
|
|
||||||
$payment = $mollie->payments->create($paymentData);
|
|
||||||
// Update meta in aparte call om de eerste POST klein te houden
|
|
||||||
$woocommerce->put("orders/{$order->id}", ['meta_data' => [['key' => '_transaction_id', 'value' => $payment->id]]]);
|
|
||||||
|
|
||||||
echo json_encode(['payment_url' => $payment->getCheckoutUrl()]);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
http_response_code(422); echo json_encode(['error' => $e->getMessage()]);
|
|
||||||
}
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- OVERIGE ACTIES (POSTCODE, PRODUCTS, LOGOUT) ---
|
|
||||||
if ($action === 'get_payment_methods') {
|
if ($action === 'get_payment_methods') {
|
||||||
try {
|
try {
|
||||||
$gateways = $woocommerce->get('payment_gateways');
|
$gateways = $woocommerce->get('payment_gateways');
|
||||||
$output = [];
|
$output = [];
|
||||||
foreach ($gateways as $gw) {
|
foreach ($gateways as $gw) {
|
||||||
|
// Filter Apple Pay en Google Pay hier volledig weg
|
||||||
|
if (str_contains($gw->id, 'applepay') || str_contains($gw->id, 'googlepay') || str_contains($gw->id, 'riverty') || str_contains($gw->id, 'klarna')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if ($gw->enabled && (str_contains($gw->id, 'mollie') || str_contains($gw->id, 'riverty') || str_contains($gw->id, 'klarna'))) {
|
if ($gw->enabled && (str_contains($gw->id, 'mollie') || str_contains($gw->id, 'riverty') || str_contains($gw->id, 'klarna'))) {
|
||||||
$output[] = ['id' => $gw->id, 'title' => $gw->method_title];
|
$output[] = ['id' => $gw->id, 'title' => $gw->method_title];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo json_encode($output);
|
echo json_encode($output);
|
||||||
} catch (Exception $e) { echo json_encode([]); }
|
} catch (Exception $e) {
|
||||||
|
echo json_encode([]);
|
||||||
|
}
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,16 +88,108 @@ if ($action === 'get_products') {
|
|||||||
$enriched[] = $p;
|
$enriched[] = $p;
|
||||||
}
|
}
|
||||||
echo json_encode($enriched);
|
echo json_encode($enriched);
|
||||||
} catch (Exception $e) { echo json_encode([]); }
|
} catch (Exception $e) {
|
||||||
|
echo json_encode([]);
|
||||||
|
}
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($action === 'postcode_check') {
|
if ($action === 'postcode_check') {
|
||||||
$postcode = str_replace(' ', '', $_GET['postcode']);
|
$postcode = str_replace(' ', '', $_GET['postcode']);
|
||||||
$url = "https://postcode.tech/api/v1/postcode?postcode={$postcode}&number=" . $_GET['number'];
|
$url = "https://postcode.tech/api/v1/postcode?postcode={$postcode}&number=" . $_GET['number'];
|
||||||
$ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
$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_HTTPHEADER, ["Authorization: Bearer " . $_ENV['POSTCODE_TECH_KEY']]);
|
||||||
echo curl_exec($ch); exit;
|
echo curl_exec($ch);
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($action === 'logout') { session_destroy(); echo json_encode(['success' => true]); exit; }
|
// --- 3. CREATE ORDER (V7.0 - INCL. 100 EURO THRESHOLD) ---
|
||||||
|
if ($action === 'create_order') {
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
try {
|
||||||
|
$mediacode = $input['mediacode_internal'] ?? 'Geen';
|
||||||
|
$wc_gateway_id = $input['payment_method'];
|
||||||
|
$mollie_method = str_replace(['mollie_wc_gateway_', 'rve_'], '', $wc_gateway_id);
|
||||||
|
|
||||||
|
$input['payment_method'] = $wc_gateway_id;
|
||||||
|
$input['customer_note'] = "Agent: {$_SESSION['user']} | Mediacode: $mediacode";
|
||||||
|
|
||||||
|
// Attribution data voor herkomst-blokje
|
||||||
|
$input['meta_data'][] = ['key' => '_wc_order_attribution_source_type', 'value' => 'utm'];
|
||||||
|
$input['meta_data'][] = ['key' => '_wc_order_attribution_utm_source', 'value' => 'SalesPanel'];
|
||||||
|
$input['meta_data'][] = ['key' => '_wc_order_attribution_utm_campaign', 'value' => $mediacode];
|
||||||
|
$input['meta_data'][] = ['key' => '_wc_order_attribution_origin', 'value' => 'Sales Panel'];
|
||||||
|
$input['meta_data'][] = ['key' => 'Mediacode', 'value' => $mediacode];
|
||||||
|
|
||||||
|
// A. Order aanmaken in WooCommerce
|
||||||
|
$order = $woocommerce->post('orders', $input);
|
||||||
|
|
||||||
|
// EXTRA CHECK: Termijn betalingen pas vanaf 100 euro
|
||||||
|
if (in_array($mollie_method, ['in3', 'klarna', 'klarnapaylater', 'klarnasliceit']) && (float)$order->total < 100.00) {
|
||||||
|
$woocommerce->delete("orders/{$order->id}", ['force' => true]);
|
||||||
|
throw new Exception("Termijnbetaling (in3/Klarna) pas mogelijk vanaf €100,-");
|
||||||
|
}
|
||||||
|
|
||||||
|
$mollie = new MollieApiClient();
|
||||||
|
$mollie->setApiKey($_ENV['MOLLIE_KEY']);
|
||||||
|
$is_sub = (stripos(json_encode($order->line_items), 'abonnement') !== false);
|
||||||
|
|
||||||
|
// B. Voorbereiden Mollie Data
|
||||||
|
$paymentData = [
|
||||||
|
"amount" => ["currency" => "EUR", "value" => ($mollie_method === 'ideal' && $is_sub) ? "0.01" : number_format((float)$order->total, 2, '.', '')],
|
||||||
|
"description" => "Order #{$order->id}",
|
||||||
|
"redirectUrl" => $_ENV['WC_URL'] . "/checkout/order-received/{$order->id}/?key={$order->order_key}&order_id={$order->id}&utm_source=SalesPanel&utm_campaign={$mediacode}",
|
||||||
|
"webhookUrl" => $_ENV['WC_URL'] . "/wc-api/{$wc_gateway_id}/?key={$order->order_key}&order_id={$order->id}",
|
||||||
|
"method" => $mollie_method,
|
||||||
|
"metadata" => ["order_id" => (string)$order->id, "mediacode" => $mediacode]
|
||||||
|
];
|
||||||
|
|
||||||
|
// C. "Light Lines" fix voor Server Error 500 / Klarna-validatie
|
||||||
|
if (in_array($mollie_method, ['in3', 'klarna', 'klarnapaylater', 'klarnasliceit', 'riverty'])) {
|
||||||
|
if ($mollie_method === 'riverty') {
|
||||||
|
$paymentData["captureMode"] = "manual";
|
||||||
|
}
|
||||||
|
$paymentData["billingAddress"] = [
|
||||||
|
"givenName" => $input['billing']['first_name'],
|
||||||
|
"familyName" => $input['billing']['last_name'],
|
||||||
|
"email" => $input['billing']['email'],
|
||||||
|
"streetAndNumber" => $input['billing']['address_1'],
|
||||||
|
"city" => $input['billing']['city'],
|
||||||
|
"postalCode" => $input['billing']['postcode'],
|
||||||
|
"country" => "NL"
|
||||||
|
];
|
||||||
|
$paymentData["lines"] = [[
|
||||||
|
"name" => "Bestelling #" . $order->id,
|
||||||
|
"quantity" => 1,
|
||||||
|
"unitPrice" => ["currency" => "EUR", "value" => number_format((float)$order->total, 2, '.', '')],
|
||||||
|
"totalAmount" => ["currency" => "EUR", "value" => number_format((float)$order->total, 2, '.', '')],
|
||||||
|
"vatRate" => "21.00",
|
||||||
|
"vatAmount" => ["currency" => "EUR", "value" => number_format((float)$order->total_tax, 2, '.', '')]
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// D. Mollie betaling aanmaken
|
||||||
|
$payment = $mollie->payments->create($paymentData);
|
||||||
|
|
||||||
|
// E. Update Mollie ID in WooCommerce
|
||||||
|
$woocommerce->put("orders/{$order->id}", ['meta_data' => [['key' => '_mollie_payment_id', 'value' => $payment->id], ['key' => '_transaction_id', 'value' => $payment->id], ['key' => '_mediacode', 'value' => $mediacode]]]);
|
||||||
|
|
||||||
|
$woocommerce->post("orders/{$order->id}/notes", [
|
||||||
|
'note' => "Betaallink gegenereerd: " . $payment->getCheckoutUrl(),
|
||||||
|
'customer_note' => true // Dit stuurt een mail naar de klant met de link!
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo json_encode(['payment_url' => $payment->getCheckoutUrl()]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(422);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'logout') {
|
||||||
|
session_destroy();
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|||||||
61
index.html
61
index.html
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<div x-show="isLoggedIn" x-cloak class="max-w-[1440px] mx-auto p-6">
|
<div x-show="isLoggedIn" x-cloak class="max-w-[1440px] mx-auto p-6">
|
||||||
<header class="flex justify-between items-center mb-8 bg-white p-6 rounded-3xl shadow-sm border-b-4 border-blue-600">
|
<header class="flex justify-between items-center mb-8 bg-white p-6 rounded-3xl shadow-sm border-b-4 border-blue-600">
|
||||||
<h1 class="text-2xl font-black italic tracking-tighter">TELVERO <span class="text-blue-600">PANEL</span></h1>
|
<h1 class="text-2xl font-black italic">TELVERO <span class="text-blue-600">PANEL</span></h1>
|
||||||
<div class="flex items-center gap-6 text-sm font-bold text-slate-400">
|
<div class="flex items-center gap-6 text-sm font-bold text-slate-400">
|
||||||
<span x-text="'Agent: ' + currentUser"></span>
|
<span x-text="'Agent: ' + currentUser"></span>
|
||||||
<button @click="doLogout()" class="text-red-500 underline uppercase text-xs font-black">Uitloggen</button>
|
<button @click="doLogout()" class="text-red-500 underline uppercase text-xs font-black">Uitloggen</button>
|
||||||
@ -54,12 +54,12 @@
|
|||||||
<input type="text" x-model="form.street" placeholder="Straat" class="w-full border p-3 rounded-xl bg-slate-100 font-bold text-xs shadow-inner" readonly>
|
<input type="text" x-model="form.street" placeholder="Straat" class="w-full border p-3 rounded-xl bg-slate-100 font-bold text-xs shadow-inner" readonly>
|
||||||
<input type="text" x-model="form.city" placeholder="Stad" class="w-full border p-3 rounded-xl bg-slate-100 font-bold text-xs shadow-inner" readonly>
|
<input type="text" x-model="form.city" placeholder="Stad" class="w-full border p-3 rounded-xl bg-slate-100 font-bold text-xs shadow-inner" readonly>
|
||||||
<input type="tel" x-model="form.phone" placeholder="Telefoon (06...)" class="border p-3 rounded-xl w-full focus:border-blue-500 outline-none">
|
<input type="tel" x-model="form.phone" placeholder="Telefoon (06...)" class="border p-3 rounded-xl w-full focus:border-blue-500 outline-none">
|
||||||
<input type="email" x-model="form.email" placeholder="E-mail (Verplicht)" class="border-2 border-amber-300 p-3 rounded-xl w-full outline-none focus:border-amber-500">
|
<input type="email" x-model="form.email" placeholder="E-mail (Verplicht)" class="border-2 border-amber-300 p-3 rounded-xl w-full outline-none focus:border-amber-500 shadow-sm">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-12 lg:col-span-5 bg-white p-8 rounded-[2rem] shadow-sm border border-slate-200">
|
<div class="col-span-12 lg:col-span-5 bg-white p-8 rounded-[2rem] shadow-sm border border-slate-200">
|
||||||
<h2 class="font-bold mb-6 text-slate-400 uppercase text-[10px] tracking-widest border-b pb-2 text-center italic">Producten</h2>
|
<h2 class="font-bold mb-6 text-slate-400 uppercase text-[10px] tracking-widest border-b pb-2 text-center italic">Selectie</h2>
|
||||||
<select x-model="selectedProductId" @change="selectProduct()" class="w-full border-2 border-slate-100 p-5 rounded-2xl font-black text-slate-700 mb-6 bg-slate-50 outline-none focus:border-blue-500 shadow-sm">
|
<select x-model="selectedProductId" @change="selectProduct()" class="w-full border-2 border-slate-100 p-5 rounded-2xl font-black text-slate-700 mb-6 bg-slate-50 outline-none focus:border-blue-500 shadow-sm">
|
||||||
<option value="">-- Kies Hoofdproduct --</option>
|
<option value="">-- Kies Hoofdproduct --</option>
|
||||||
<template x-for="p in products" :key="p.id">
|
<template x-for="p in products" :key="p.id">
|
||||||
@ -67,9 +67,10 @@
|
|||||||
</template>
|
</template>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<div x-show="variations.length > 0" x-cloak class="mb-8 p-6 bg-blue-50 rounded-3xl border border-blue-100 shadow-inner">
|
<div x-show="variations.length > 0" x-cloak class="mb-8 p-6 bg-blue-50 rounded-3xl border border-blue-100 shadow-inner text-center">
|
||||||
|
<label class="block text-[10px] font-black text-blue-600 uppercase mb-3 tracking-widest italic">Kies Optie</label>
|
||||||
<select x-model="selectedVariationId" @change="selectVariation()" class="w-full border-2 border-white p-4 rounded-2xl font-bold bg-white text-slate-700 shadow-sm">
|
<select x-model="selectedVariationId" @change="selectVariation()" class="w-full border-2 border-white p-4 rounded-2xl font-bold bg-white text-slate-700 shadow-sm">
|
||||||
<option value="">-- Kies Optie --</option>
|
<option value="">-- Maak een keuze --</option>
|
||||||
<template x-for="v in variations" :key="v.id">
|
<template x-for="v in variations" :key="v.id">
|
||||||
<option :value="v.id" x-text="getVarName(v) + ' (€' + v.price + ')'"></option>
|
<option :value="v.id" x-text="getVarName(v) + ' (€' + v.price + ')'"></option>
|
||||||
</template>
|
</template>
|
||||||
@ -77,7 +78,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div x-show="upsellOptions.length > 0" x-cloak class="space-y-3">
|
<div x-show="upsellOptions.length > 0" x-cloak class="space-y-3">
|
||||||
<p class="text-[10px] font-black text-red-500 uppercase tracking-widest italic px-2">Aanbevolen extra's</p>
|
<p class="text-[10px] font-black text-red-500 uppercase tracking-widest italic px-2 text-center">Upsell Suggesties</p>
|
||||||
<template x-for="u in upsellOptions" :key="u.id">
|
<template x-for="u in upsellOptions" :key="u.id">
|
||||||
<div class="flex items-center justify-between p-4 border rounded-2xl bg-slate-50 hover:bg-white transition-all shadow-sm border-slate-100">
|
<div class="flex items-center justify-between p-4 border rounded-2xl bg-slate-50 hover:bg-white transition-all shadow-sm border-slate-100">
|
||||||
<span class="text-xs font-bold text-slate-700" x-text="u.name + ' (€' + u.price + ')'"></span>
|
<span class="text-xs font-bold text-slate-700" x-text="u.name + ' (€' + u.price + ')'"></span>
|
||||||
@ -93,25 +94,23 @@
|
|||||||
<div class="space-y-4 mb-8 min-h-[100px] max-h-[300px] overflow-y-auto pr-2 custom-scrollbar">
|
<div class="space-y-4 mb-8 min-h-[100px] max-h-[300px] overflow-y-auto pr-2 custom-scrollbar">
|
||||||
<template x-for="(item, index) in cart" :key="index">
|
<template x-for="(item, index) in cart" :key="index">
|
||||||
<div class="flex justify-between items-center group">
|
<div class="flex justify-between items-center group">
|
||||||
<div class="flex flex-col flex-1 pr-2">
|
<div class="flex flex-col flex-1">
|
||||||
<span x-text="item.name" class="text-[11px] font-medium leading-tight text-slate-300"></span>
|
<span x-text="item.name" class="text-[11px] font-medium leading-tight text-slate-300"></span>
|
||||||
<span x-text="'€' + item.price" class="text-[11px] font-black text-blue-400"></span>
|
<span x-text="'€' + item.price" class="text-[11px] font-black text-blue-400"></span>
|
||||||
</div>
|
</div>
|
||||||
<button @click="removeFromCart(index)" class="text-slate-600 hover:text-red-500 transition-colors">
|
<button @click="removeFromCart(index)" class="text-slate-600 hover:text-red-500 transition-colors p-1">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-8 pt-4 border-t border-slate-800 space-y-2">
|
<div class="mb-8 pt-4 border-t border-slate-800 space-y-2 text-center">
|
||||||
<p class="text-[10px] text-slate-500 uppercase font-black mb-3 italic tracking-widest text-center">Betaling</p>
|
<p class="text-[10px] text-slate-500 uppercase font-black mb-3 italic tracking-widest">Betaalwijze</p>
|
||||||
<div class="space-y-2 max-h-[200px] overflow-y-auto pr-2 custom-scrollbar">
|
<div class="space-y-2 max-h-[250px] overflow-y-auto pr-2 custom-scrollbar">
|
||||||
<template x-for="method in paymentMethods" :key="method.id">
|
<template x-for="method in filteredPaymentMethods" :key="method.id">
|
||||||
<button @click="payment_method = method.id"
|
<button @click="payment_method = method.id"
|
||||||
:class="payment_method === method.id ? 'bg-blue-600 border-blue-400 ring-2 ring-blue-500/50' : 'bg-slate-800 border-slate-700 opacity-60'"
|
:class="payment_method === method.id ? 'bg-blue-600 border-blue-400' : 'bg-slate-800 border-slate-700 opacity-60'"
|
||||||
class="w-full text-left p-3 rounded-2xl border flex items-center gap-3 transition-all duration-200">
|
class="w-full text-left p-3 rounded-2xl border flex items-center gap-3 transition-all duration-200">
|
||||||
<img :src="method.image" class="w-7 h-7 rounded bg-white p-1 shadow-inner">
|
<img :src="method.image" class="w-7 h-7 rounded bg-white p-1 shadow-inner">
|
||||||
<span class="text-[10px] font-black uppercase text-white tracking-tighter" x-text="method.title"></span>
|
<span class="text-[10px] font-black uppercase text-white tracking-tighter" x-text="method.title"></span>
|
||||||
@ -120,16 +119,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-between items-center mb-8 pt-4 border-t border-slate-800">
|
<div class="flex justify-between items-center mb-8 pt-4 border-t border-slate-800 text-right">
|
||||||
<span class="text-3xl font-black text-green-400" x-text="'€' + total"></span>
|
<span class="text-3xl font-black text-green-400" x-text="'€' + total"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button @click="submitOrder()"
|
<button @click="submitOrder()"
|
||||||
:disabled="submitting || !form.email || !meta.mediacode || cart.length === 0"
|
:disabled="submitting || !form.email || !meta.mediacode || cart.length === 0"
|
||||||
class="w-full bg-blue-600 hover:bg-blue-500 p-6 rounded-2xl font-black text-lg shadow-xl disabled:opacity-20 uppercase tracking-tighter transition active:scale-95 shadow-blue-500/20">
|
class="w-full bg-blue-600 hover:bg-blue-500 p-6 rounded-2xl font-black text-lg shadow-xl disabled:opacity-20 uppercase tracking-tighter transition active:scale-95 shadow-blue-500/20">
|
||||||
<span x-text="submitting ? 'PROCESSING...' : 'ORDER VERSTUREN'"></span>
|
<span x-text="submitting ? 'PROCESSING...' : 'ORDER BEVESTIGEN'"></span>
|
||||||
</button>
|
</button>
|
||||||
<p x-show="!meta.mediacode" class="text-[9px] text-red-400 mt-4 text-center font-bold italic animate-pulse">Mediacode verplicht!</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -168,10 +166,20 @@
|
|||||||
if (m.id.includes('in3')) iconKey = 'in3';
|
if (m.id.includes('in3')) iconKey = 'in3';
|
||||||
return { ...m, image: `https://www.mollie.com/external/icons/payment-methods/${iconKey}.svg` };
|
return { ...m, image: `https://www.mollie.com/external/icons/payment-methods/${iconKey}.svg` };
|
||||||
});
|
});
|
||||||
if(this.paymentMethods.length > 0) {
|
if(this.paymentMethods.length > 0) this.payment_method = 'mollie_wc_gateway_ideal';
|
||||||
const ideal = this.paymentMethods.find(m => m.id.includes('ideal'));
|
},
|
||||||
this.payment_method = ideal ? ideal.id : this.paymentMethods[0].id;
|
|
||||||
|
// 100 EURO DREMPEL LOGICA
|
||||||
|
get filteredPaymentMethods() {
|
||||||
|
return this.paymentMethods.filter(method => {
|
||||||
|
const isTermin = method.id.includes('in3') || method.id.includes('klarna');
|
||||||
|
const currentTotal = parseFloat(this.total);
|
||||||
|
if (isTermin && currentTotal < 100) {
|
||||||
|
if (this.payment_method === method.id) this.payment_method = 'mollie_wc_gateway_ideal';
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
selectProduct() {
|
selectProduct() {
|
||||||
@ -200,18 +208,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
removeFromCart(index) {
|
removeFromCart(index) { this.cart.splice(index, 1); },
|
||||||
this.cart.splice(index, 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
getVarName(v) { return v.attributes.map(a => a.option).join(' '); },
|
getVarName(v) { return v.attributes.map(a => a.option).join(' '); },
|
||||||
|
|
||||||
toggleUpsell(u) {
|
toggleUpsell(u) {
|
||||||
const idx = this.cart.findIndex(i => parseInt(i.id) === parseInt(u.id));
|
const idx = this.cart.findIndex(i => parseInt(i.id) === parseInt(u.id));
|
||||||
if(idx > -1) { this.cart.splice(idx, 1); }
|
if(idx > -1) { this.cart.splice(idx, 1); }
|
||||||
else { this.cart.push({ id: parseInt(u.id), name: u.name, price: u.price }); }
|
else { this.cart.push({ id: parseInt(u.id), name: u.name, price: u.price }); }
|
||||||
},
|
},
|
||||||
|
|
||||||
isInCart(id) { return this.cart.some(i => parseInt(i.id) === parseInt(id)); },
|
isInCart(id) { return this.cart.some(i => parseInt(i.id) === parseInt(id)); },
|
||||||
get total() { return this.cart.reduce((sum, item) => sum + parseFloat(item.price), 0).toFixed(2); },
|
get total() { return this.cart.reduce((sum, item) => sum + parseFloat(item.price), 0).toFixed(2); },
|
||||||
|
|
||||||
@ -229,7 +232,7 @@
|
|||||||
try {
|
try {
|
||||||
const res = await fetch('api.php?action=create_order', { method: 'POST', body: JSON.stringify(payload) });
|
const res = await fetch('api.php?action=create_order', { method: 'POST', body: JSON.stringify(payload) });
|
||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
if(result.payment_url) { alert("Succes! De order is aangemaakt."); this.cart = []; this.selectedProductId = ''; this.form = { initials: '', lastname: '', postcode: '', houseno: '', suffix: '', street: '', city: '', email: '', dob: '', phone: '' }; }
|
if(result.payment_url) { alert("Succes! Order aangemaakt."); this.cart = []; this.selectedProductId = ''; this.form = { initials: '', lastname: '', postcode: '', houseno: '', suffix: '', street: '', city: '', email: '', dob: '', phone: '' }; }
|
||||||
else { alert("Fout: " + result.error); }
|
else { alert("Fout: " + result.error); }
|
||||||
} catch(e) { alert("Systeemfout"); }
|
} catch(e) { alert("Systeemfout"); }
|
||||||
this.submitting = false;
|
this.submitting = false;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user