feat(payments): Express-Start mit Versand und Zahlartaufschlag, PATCH nur bei Abweichung

PayPal Live verlangt nach jeder Betragsaenderung eine zweite Freigabe (422
PAYER_ACTION_REQUIRED). Die Kasse rechnet den Endbetrag jetzt beim Express-Start vor
(erste aktive Versandart des Gebiets, Gast = Gebiet 1, eingeloggt = Land/Gruppe), der
Provider vergleicht vor dem PATCH mit dem Stand bei PayPal und laesst ihn bei Gleichstand
aus. Webhooks ordnen ohne custom_id ueber die PayPal-Order-ID zu.
This commit is contained in:
Thomas Bartelt
2026-09-18 07:40:27 +02:00
parent c8fc7fba2f
commit f65901060f
2 changed files with 93 additions and 14 deletions
+60 -1
View File
@@ -390,7 +390,7 @@ class website_cashdesk {
exit;
}
try {
$url = $provider->startExpress(round((float)$cart['price_sum_gross'], 2));
$url = $provider->startExpress($this->payment_express_amount($cart, $provider));
} catch (Throwable $e) {
error_log('Payments express start '.$module.': '.$e->getMessage());
$this->layout_object->assign('call', 'payment_error');
@@ -402,6 +402,65 @@ class website_cashdesk {
exit;
}
/**
* 18.09.2026 -- Endbetrag fuer den Express-Start vorab rechnen: Warenwert + Versand +
* Zahlartaufschlag, genau so, wie die Kasse ihn spaeter vorbelegt (erste aktive Versandart
* des Gebiets, Gast: Gebiet 1 wie in manage_guest_payment_method). PayPal Live verlangt
* bei einer Betragsaenderung nach der Freigabe eine zweite Freigabe (422
* PAYER_ACTION_REQUIRED); stimmt der Betrag schon, entfaellt der PATCH und damit der
* zweite PayPal-Besuch. Land: erstes erlaubtes Express-Land (Setting, Standard DE).
* Im Zweifel (Fehler, keine Versandart) bleibt es beim Warenwert -- dann greift der
* Rueckfall mit zweiter Freigabe wie bisher.
*/
private function payment_express_amount($cart, $provider) {
$amount = round((float)$cart['price_sum_gross'], 2);
try {
$guest_group = (int)$this->base_object->config->shopConfiguration['guest_order_customer_group'];
$guest_id = (int)$this->base_object->config->shopConfiguration['guest_order_customer_id'];
$cid = (int)$this->base_object->customer->customer_id;
$logged_in = $cid > 0 && $cid !== $guest_id;
$group_id = $guest_group;
$area_id = 1;
if ($logged_in) {
$customer_object = new Customer($this->base_object);
$customer_data = $customer_object->get_by_id($cid);
$group_id = (int)$customer_data->group_id;
$allowed = array_filter(array_map('trim', explode(',', strtoupper($provider->expressCountries()))));
$iso = $allowed ? reset($allowed) : 'DE';
$country_id = $provider->countryIdByIso($iso);
$area_id = $country_id > 0 ? (int)Country::getShippingAreaById($country_id, $group_id) : 0;
if ($area_id <= 0) {
return $amount;
}
}
$deliverer = new Deliverer($this->base_object);
$methods = $logged_in
? $deliverer->getAllActive($area_id, $cart['standard_shipping_items'], $cart['bulk_goods'], $cart['item_weight'], $cart['price_sum_gross'])
: $deliverer->getAllActive($area_id, $cart['standard_shipping_items'], $cart['bulk_goods'], $cart['item_weight']);
if (!is_array($methods) || !$methods) {
return $amount;
}
$first = current($methods);
$shipping = round((float)(isset($first->price_add) ? $first->price_add : 0), 2);
$payment_add = 0.0;
$payment_method_object = new Payment_method($this->base_object);
$pms = $payment_method_object->getMethodsForGroup($group_id, $cart['price_sum_gross'], $area_id);
foreach ((array)$pms as $pm) {
if (isset($pm->module) && $pm->module === $provider->module()) {
$payment_add = round((float)(isset($pm->paymentAdd) ? $pm->paymentAdd : 0), 2);
break;
}
}
return round($amount + $shipping + $payment_add, 2);
} catch (Throwable $e) {
error_log('Payments express amount: '.$e->getMessage());
return $amount;
}
}
/** Rueckkehr im Express-Weg: /Kasse/?payment_express=<modul>&token=... */
private function payment_express_return() {
$module = strtolower(trim((string)$_GET['payment_express']));