diff --git a/info.xml b/info.xml index 874976e..046f379 100644 --- a/info.xml +++ b/info.xml @@ -1,8 +1,8 @@ mollie - Zahlungsartplugin für mollie.com + Zahlungsartplugin für mollie.com WebStollen - http://www.webstollen.de/ + https://www.webstollen.de/ 102 ws_mollie 405 @@ -26,6 +26,9 @@ 2019-11-26 + + 2019-12-18 + 131_globalinclude.php 144_notify.php @@ -49,12 +52,12 @@ Einstellungen API Key: - Füge hier deinen mollie API Key ein + Füge hier deinen mollie API Key ein api_key Profile ID: - Füge hier deinen mollie Profil ID ein. Wird benötigt um mollie Components zu aktivieren + Füge hier deinen mollie Profil ID (pfl_) ein. Wird benötigt um mollie Components zu aktivieren profileId @@ -68,18 +71,18 @@ Zahlungsart Daten syncronisiernen - Lädt Name / Bild für die jeweilige Sprache automatisch von mollie, und speichert diese in der Datenbank + Lädt Name / Bild für die jeweilige Sprache automatisch von mollie, und speichert diese in der Datenbank paymentmethod_sync - - + + Checkout Styles laden - Lädt Stylesheets für das Evo Template, um den Checkout zu verschönern. + Lädt Stylesheets für das Evo Template, um den Checkout zu verschönern. load_styles @@ -89,7 +92,7 @@ TransaktionsID des Zahlungseingangs: - Welche ID soll an die WAWI übetragen werden? + Welche ID soll an die WAWI übetragen werden? wawiPaymentID @@ -99,7 +102,7 @@ Workflow-Secret: - Schlüssel, um die WAWI Workflow-Requests zu authentifizieren. + Schlüssel, um die WAWI Workflow-Requests zu authentifizieren. workflowSecret @@ -114,7 +117,7 @@ error_canceled - error_open error_failed cvchint_1 - - + + cvchint_2 - - @@ -214,7 +217,6 @@ mollie Kreditkarte 3 - tpl/mollieComponents.tpl 0 mollie.com CREDIT_CARD @@ -286,9 +288,9 @@ JTLMollieBanktransfer tpl/bestellabschluss.tpl - SEPA Ãœberweisung + SEPA Überweisung mollie - Bezahlen Sie bequem mit SEPA Ãœberweisung. + Bezahlen Sie bequem mit SEPA Überweisung. @@ -553,4 +555,4 @@ - + \ No newline at end of file diff --git a/version/103/paymentmethod/JTLMollie.php b/version/103/paymentmethod/JTLMollie.php index 0d23b1d..7733a27 100644 --- a/version/103/paymentmethod/JTLMollie.php +++ b/version/103/paymentmethod/JTLMollie.php @@ -141,7 +141,7 @@ public function preparePaymentProcess($order) } $logData .= '$' . $oMolliePayment->id; $this->doLog('Mollie Create Payment Redirect: ' . $oMolliePayment->getCheckoutUrl() . "
" . print_r($oMolliePayment, 1) . "
", $logData, LOGLEVEL_DEBUG); - Payment::updateFromPayment($oMolliePayment, $order->kBestellung, md5($hash)); + Payment::updateFromPayment($oMolliePayment, $order->kBestellung, md5(trim($hash, '_'))); Shop::Smarty()->assign('oMolliePayment', $oMolliePayment); if (!$this->duringCheckout) { Session::getInstance()->cleanUp(); diff --git a/version/106/adminmenu/info.php b/version/106/adminmenu/info.php new file mode 100644 index 0000000..292064e --- /dev/null +++ b/version/106/adminmenu/info.php @@ -0,0 +1,64 @@ +assign('defaultTabbertab', Helper::getAdminmenu('Info')); + Helper::selfupdate(); + } + + $svgQuery = http_build_query([ + 'p' => Helper::oPlugin()->cPluginID, + 'v' => Helper::oPlugin()->nVersion, + 's' => defined('APPLICATION_VERSION') ? APPLICATION_VERSION : JTL_VERSION, + 'b' => defined('JTL_MINOR_VERSION') ? JTL_MINOR_VERSION : '0', + 'd' => Helper::getDomain(), + 'm' => base64_encode(Helper::getMasterMail(true)), + 'php' => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION . PHP_EXTRA_VERSION, + ]); + + echo ""; + echo "
" . + "
" . + " " . + " Lizenz Informationen" . + ' ' . + '
' . + "
" . + " " . + " Update Informationen" . + ' ' . + '
' . + "
" . + " " . + " Plugin informationen" . + ' ' . + '
' . + '
'; + + try { + $latestRelease = Helper::getLatestRelease(array_key_exists('update', $_REQUEST)); + if ((int)Helper::oPlugin()->nVersion < (int)$latestRelease->version) { + Shop::Smarty()->assign('update', $latestRelease); + } + + } catch (\Exception $e) { + } + + Shop::Smarty()->display(Helper::oPlugin()->cAdminmenuPfad . '/tpl/info.tpl'); + + if (file_exists(__DIR__ . '/_addon.php')) { + try { + include __DIR__ . '/_addon.php'; + } catch (Exception $e) { + } + } + +} catch (Exception $e) { + echo "
Fehler: {$e->getMessage()}
"; + Helper::logExc($e); +} \ No newline at end of file diff --git a/version/106/adminmenu/orders.php b/version/106/adminmenu/orders.php new file mode 100644 index 0000000..96a1207 --- /dev/null +++ b/version/106/adminmenu/orders.php @@ -0,0 +1,154 @@ + 'danger', 'text' => 'Keine ID angeben!']; + break; + } + $payment = Payment::getPaymentMollie($_REQUEST['id']); + if (!$payment) { + $ordersMsgs[] = (object)['type' => 'danger', 'text' => 'Order nicht gefunden!']; + break; + } + + $order = JTLMollie::API()->orders->get($_REQUEST['id']); + if ($order->status == OrderStatus::STATUS_CANCELED) { + $ordersMsgs[] = (object)['type' => 'danger', 'text' => 'Bestellung bereits storniert']; + break; + } + $refund = JTLMollie::API()->orderRefunds->createFor($order, ['lines' => []]); + Mollie::JTLMollie()->doLog("Order refunded:
" . print_r($refund, 1) . "
", '$' . $payment->kID . '#' . $payment->kBestellung . '§' . $payment->cOrderNumber, LOGLEVEL_NOTICE); + + goto order; + break; + + case 'cancel': + if (!array_key_exists('id', $_REQUEST)) { + $ordersMsgs[] = (object)['type' => 'danger', 'text' => 'Keine ID angeben!']; + break; + } + $payment = Payment::getPaymentMollie($_REQUEST['id']); + if (!$payment) { + $ordersMsgs[] = (object)['type' => 'danger', 'text' => 'Order nicht gefunden!']; + break; + } + $order = JTLMollie::API()->orders->get($_REQUEST['id']); + if ($order->status == OrderStatus::STATUS_CANCELED) { + $ordersMsgs[] = (object)['type' => 'danger', 'text' => 'Bestellung bereits storniert']; + break; + } + $cancel = JTLMollie::API()->orders->cancel($order->id); + Mollie::JTLMollie()->doLog("Order canceled:
" . print_r($cancel, 1) . "
", '$' . $payment->kID . '#' . $payment->kBestellung . '§' . $payment->cOrderNumber, LOGLEVEL_NOTICE); + goto order; + break; + + case 'capture': + if (!array_key_exists('id', $_REQUEST)) { + $ordersMsgs[] = (object)['type' => 'danger', 'text' => 'Keine ID angeben!']; + break; + } + $payment = Payment::getPaymentMollie($_REQUEST['id']); + if (!$payment) { + $ordersMsgs[] = (object)['type' => 'danger', 'text' => 'Order nicht gefunden!']; + break; + } + $order = JTLMollie::API()->orders->get($_REQUEST['id']); + if ($order->status !== OrderStatus::STATUS_AUTHORIZED && $order->status !== OrderStatus::STATUS_SHIPPING) { + $ordersMsgs[] = (object)['type' => 'danger', 'text' => 'Nur autorisierte Zahlungen können erfasst werden!']; + break; + } + + $oBestellung = new Bestellung($payment->kBestellung, true); + if (!$oBestellung->kBestellung) { + $ordersMsgs[] = (object)['type' => 'danger', 'text' => 'Bestellung konnte nicht geladen werden!']; + break; + } + + $logData = '#' . $payment->kBestellung . '$' . $payment->kID . "§" . $oBestellung->cBestellNr; + + $options = ['lines' => []]; + if ($oBestellung->cTracking) { + $tracking = new stdClass(); + $tracking->carrier = $oBestellung->cVersandartName; + $tracking->url = $oBestellung->cTrackingURL; + $tracking->code = $oBestellung->cTracking; + $options['tracking'] = $tracking; + } + + // CAPTURE ALL + $shipment = JTLMollie::API()->shipments->createFor($order, $options); + $ordersMsgs[] = (object)['type' => 'success', 'text' => 'Zahlung wurde erfolgreich erfasst!']; + Mollie::JTLMollie()->doLog('Shipment created
' . print_r(['options' => $options, 'shipment' => $shipment], 1) . '
', $logData); + goto order; + + case 'order': + order: + if (!array_key_exists('id', $_REQUEST)) { + $ordersMsgs[] = (object)['type' => 'danger', 'text' => 'Keine ID angeben!']; + break; + } + + $order = JTLMollie::API()->orders->get($_REQUEST['id'], ['embed' => 'payments,refunds']); + $payment = Payment::getPaymentMollie($_REQUEST['id']); + if ($payment) { + $oBestellung = new Bestellung($payment->kBestellung, false); + //\ws_mollie\Model\Payment::updateFromPayment($order, $oBestellung->kBestellung); + if ($oBestellung->kBestellung && $oBestellung->cBestellNr !== $payment->cOrderNumber) { + Shop::DB()->executeQueryPrepared("UPDATE xplugin_ws_mollie_payments SET cOrderNumber = :cBestellNr WHERE kID = :kID", [ + ':cBestellNr' => $oBestellung->cBestellNr, + ':kID' => $payment->kID, + ], 3); + } + } + + $logs = Shop::DB()->executeQueryPrepared("SELECT * FROM tzahlungslog WHERE cLogData LIKE :kBestellung OR cLogData LIKE :cBestellNr OR cLogData LIKE :MollieID ORDER BY dDatum DESC, cLog DESC", [ + ':kBestellung' => '%#' . ($payment->kBestellung ?: '##') . '%', + ':cBestellNr' => '%§' . ($payment->cOrderNumber ?: '§§') . '%', + ':MollieID' => '%$' . ($payment->kID ?: '$$') . '%', + ], 2); + + Shop::Smarty()->assign('payment', $payment) + ->assign('oBestellung', $oBestellung) + ->assign('order', $order) + ->assign('logs', $logs) + ->assign('ordersMsgs', $ordersMsgs); + Shop::Smarty()->display($oPlugin->cAdminmenuPfad . '/tpl/order.tpl'); + return; + } + } + + + $payments = Shop::DB()->executeQueryPrepared("SELECT * FROM xplugin_ws_mollie_payments WHERE kBestellung IS NOT NULL AND cStatus != 'created' ORDER BY dCreatedAt DESC LIMIT 1000;", [], 2); + foreach ($payments as $i => $payment) { + $payments[$i]->oBestellung = new Bestellung($payment->kBestellung, false); + } + + Shop::Smarty()->assign('payments', $payments) + ->assign('ordersMsgs', $ordersMsgs) + ->assign('admRoot', str_replace('http:', '', $oPlugin->cAdminmenuPfadURL)) + ->assign('hasAPIKey', trim(Helper::getSetting("api_key")) !== ''); + + Shop::Smarty()->display($oPlugin->cAdminmenuPfad . '/tpl/orders.tpl'); +} catch (Exception $e) { + echo "
" . + "{$e->getMessage()}
" . + "
{$e->getFile()}:{$e->getLine()}
{$e->getTraceAsString()}
" . + "
"; + Helper::logExc($e); +} diff --git a/version/106/adminmenu/paymentmethods.php b/version/106/adminmenu/paymentmethods.php new file mode 100644 index 0000000..1782978 --- /dev/null +++ b/version/106/adminmenu/paymentmethods.php @@ -0,0 +1,60 @@ +setApiKey(Helper::getSetting("api_key")); + + $profile = $mollie->profiles->get('me'); + /* $methods = $mollie->methods->all([ + //'locale' => 'de_DE', + 'include' => 'pricing', + ]);*/ + + $za = filter_input(INPUT_GET, 'za', FILTER_VALIDATE_BOOLEAN); + $active = filter_input(INPUT_GET, 'active', FILTER_VALIDATE_BOOLEAN); + $amount = filter_input(INPUT_GET, 'amount', FILTER_VALIDATE_FLOAT) ?: null; + $locale = filter_input(INPUT_GET, 'locale', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^[a-zA-Z]{2}_[a-zA-Z]{2}$/']]) ?: null; + $currency = filter_input(INPUT_GET, 'currency', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^[a-zA-Z]{3}$/']]) ?: 'EUR'; + + if ($za) { + Shop::Smarty()->assign('defaultTabbertab', Helper::getAdminmenu("Zahlungsarten")); + } + + $params = ['include' => 'pricing,issuers']; + if ($amount && $currency && $locale) { + $params['amount'] = ['value' => number_format($amount, 2, '.', ''), 'currency' => $currency]; + $params['locale'] = $locale; + if ($active) { + $params['includeWallets'] = 'applepay'; + $params['resource'] = 'orders'; + } + } + + if ($active) { + $allMethods = $mollie->methods->allActive($params); + } else { + $allMethods = $mollie->methods->allAvailable($params); + } + + Shop::Smarty()->assign('profile', $profile) + ->assign('currencies', Mollie::getCurrencies()) + ->assign('locales', Mollie::getLocales()) + ->assign('allMethods', $allMethods); + Shop::Smarty()->display($oPlugin->cAdminmenuPfad . '/tpl/paymentmethods.tpl'); +} catch (Exception $e) { + echo "
{$e->getMessage()}
"; + Helper::logExc($e); +} diff --git a/version/106/adminmenu/tpl/info.tpl b/version/106/adminmenu/tpl/info.tpl new file mode 100644 index 0000000..77a48dc --- /dev/null +++ b/version/106/adminmenu/tpl/info.tpl @@ -0,0 +1,96 @@ +
+
+
+ + + +
+
+ + + +
+ + {if isset($update)} +
+ +
+

Update auf Version {$update->version} verfügbar!

+
+
+
+
Version:
+
{$update->version}
+
+
+
Erschienen:
+
{$update->create_date}
+
+
+
Changelog:
+
+ +
+
+ +
+
+ +
+
+
+ {else} +
+ + + +
+ {/if} + +
+
+
+ + + +
+
+ + + +
+ +
+
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+{if file_exists("{$smarty['current_dir']}/_addon.tpl")} + {include file="{$smarty['current_dir']}/_addon.tpl"} +{/if} \ No newline at end of file diff --git a/version/106/adminmenu/tpl/mollie-account-erstellen.png b/version/106/adminmenu/tpl/mollie-account-erstellen.png new file mode 100644 index 0000000..fc18192 Binary files /dev/null and b/version/106/adminmenu/tpl/mollie-account-erstellen.png differ diff --git a/version/106/adminmenu/tpl/order.tpl b/version/106/adminmenu/tpl/order.tpl new file mode 100644 index 0000000..5b3d172 --- /dev/null +++ b/version/106/adminmenu/tpl/order.tpl @@ -0,0 +1,286 @@ + +

+ « + Bestellung: {$oBestellung->cBestellNr} - + {if $oBestellung->cStatus|intval == 1} + OFFEN + {elseif $oBestellung->cStatus|intval == 2} + IN BEARBEITUNG + {elseif $oBestellung->cStatus|intval == 3} + BEZAHLT + {elseif $oBestellung->cStatus|intval == 4} + VERSANDT + {elseif $oBestellung->cStatus|intval == 5} + TEILVERSANDT + {elseif $oBestellung->cStatus|intval == -1} + STORNO + {else} + n/a + {/if} +

+ +{if count($ordersMsgs)} + {foreach from=$ordersMsgs item=alert} +
{$alert->text}
+ {/foreach} +
+{/if} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Mollie ID:{$payment->kID}Mode:{$order->mode}Status: + {$order->status} + {if $order->amountRefunded && $order->amountRefunded->value == $order->amount->value} + (total refund) + {elseif $order->amountRefunded && $order->amountRefunded->value > 0} + (partly refund) + {/if} +
Betrag:{$order->amount->value|number_format:2:',':''} {$order->amount->currency}Captured:{if $order->amountCaptured}{$order->amountCaptured->value|number_format:2:',':''} {$order->amountCaptured->currency}{else}-{/if}Refunded:{if $order->amountRefunded}{$order->amountRefunded->value|number_format:2:',':''} {$order->amountRefunded->currency}{else}-{/if} +
Method:{$order->method}Locale:{$order->locale}Erstellt:{"d. M Y H:i:s"|date:($order->createdAt|strtotime)}
Kunde: + {if $order->billingAddress->organizationName}{$order->billingAddress->organizationName}{else}{$order->billingAddress->title} {$order->billingAddress->givenName} {$order->billingAddress->familyName}{/if} + Zahlungslink: + {$payment->cCheckoutURL} +
+{if $order->payments()->count > 0} +

Zahlungen

+ + + + + + + + + + + + + + {foreach from=$order->payments() item=payment} + + + + + + + + + + + {/foreach} +
IDStatusMethodeAmountSettlementRefundedRemainingDetails
{$payment->id}{$payment->status}{$payment->method}{$payment->amount->value} {$payment->amount->currency} + {if $payment->settlementAmount} + {$payment->settlementAmount->value} {$payment->settlementAmount->currency} + {else}-{/if} + + {if $payment->amountRefunded} + {$payment->amountRefunded->value} {$payment->amountRefunded->currency} + {else}-{/if} + + {if $payment->amountRemaining} + {$payment->amountRemaining->value} {$payment->amountRemaining->currency} + {else}-{/if} + +
    + {foreach from=$payment->details item=value key=key} +
  • {$key}: {if $value|is_scalar}{$value}{else}{$value|json_encode}{/if}
  • + {/foreach} +
+
+{/if} + + +

Positionen:

+ +
+ {if ($order->status === 'authorized' || $order->status === 'shipping') && $oBestellung->cStatus|intval >= 3} + + Zahlung erfassen1 + + {/if} + {if !$order->amountRefunded || ($order->amount->value > $order->amountRefunded->value && $order->amountCaptured->value > 0)} + Rückerstatten2 + + {/if} + {if $order->isCancelable} + Stornieren3 + + {/if} +
+ + + + + + + + + + + + + + + + + + {assign var="vat" value=0} + {assign var="netto" value=0} + {assign var="brutto" value=0} + {foreach from=$order->lines item=line} + + {assign var="vat" value=$vat+$line->vatAmount->value} + {assign var="netto" value=$netto+$line->totalAmount->value-$line->vatAmount->value} + {assign var="brutto" value=$brutto+$line->totalAmount->value} + + + + + + + + + + + + + {/foreach} + + + + + + + + + + +
StatusSKUNameTypAnzahlMwStSteuerNettoBrutto 
+ {if $line->status == 'created'} + erstellt + {elseif $line->status == 'pending'} + austehend + {elseif $line->status == 'paid'} + bezahlt + {elseif $line->status == 'authorized'} + autorisiert + {elseif $line->status == 'shipping'} + versendet + {elseif $line->status == 'completed'} + abgeschlossen + {elseif $line->status == 'expired'} + abgelaufen + {elseif $line->status == 'canceled'} + storniert + {else} + Unbekannt: {$line->status} + {/if} + {$line->sku}{$line->name|utf8_decode}{$line->type}{$line->quantity}{$line->vatRate|floatval}%{$line->vatAmount->value|number_format:2:',':''} {$line->vatAmount->currency}{($line->totalAmount->value - $line->vatAmount->value)|number_format:2:',':''} {$line->vatAmount->currency}{$line->totalAmount->value|number_format:2:',':''} {$line->totalAmount->currency} + {*if $line->quantity > $line->quantityShipped} + + + + {/if} + {if $line->quantity > $line->quantityRefunded} + + + + {/if} + {if $line->isCancelable} + + + + {/if*} + {*$line|var_dump*} +
{$vat|number_format:2:',':''} {$order->amount->currency}{$netto|number_format:2:',':''} {$order->amount->currency}{$brutto|number_format:2:',':''} {$order->amount->currency} 
+ +
+ 1 = Bestellung wird bei Mollie als versandt markiert. WAWI wird nicht informiert.
+ 2 = Bezahlter Betrag wird dem Kunden rckerstattet. WAWI wird nicht informiert.
+ 3 = Bestellung wird bei Mollie storniert. WAWI wird nicht informiert.
+
+ +

Log

+ + + {foreach from=$logs item=log} + + + + + + + {/foreach} +
+ {if $log->nLevel == 1} + Fehler + {elseif $log->nLevel == 2} + Hinweis + {elseif $log->nLevel == 3} + Debug + {else} + unknown {$log->nLevel} + {/if} + {$log->cModulId} +
+ {$log->cLog} +
+
{$log->dDatum}
+ + diff --git a/version/106/adminmenu/tpl/orders.tpl b/version/106/adminmenu/tpl/orders.tpl new file mode 100644 index 0000000..fd1c5a8 --- /dev/null +++ b/version/106/adminmenu/tpl/orders.tpl @@ -0,0 +1,116 @@ + +{if count($ordersMsgs)} + {foreach from=$ordersMsgs item=alert} +
{$alert->text}
+ {/foreach} +
+{/if} + +{if $hasAPIKey == false} + + Jetzt kostenlos Mollie Account eröffnen! + +{else} + + + + + + + + + + + + + + + + {foreach from=$payments item=payment} + + + + + + + + + + + + {/foreach} + +
BestellNr.IDMollie StatusJTL StatusBetragWährungLocaleMethodeErstellt
+ {$payment->cOrderNumber} + {if $payment->cMode == 'test'} + TEST + {/if} + + {$payment->kID} + + {if $payment->cStatus == 'created'} + erstellt + {elseif $payment->cStatus == 'pending'} + austehend + {elseif $payment->cStatus == 'paid'} + bezahlt + {elseif $payment->cStatus == 'authorized'} + autorisiert + {elseif $payment->cStatus == 'shipping'} + versendet + {elseif $payment->cStatus == 'completed'} + abgeschlossen + {elseif $payment->cStatus == 'expired'} + abgelaufen + {elseif $payment->cStatus == 'canceled'} + storniert + {else} + Unbekannt: {$payment->cStatus} + {/if} + {if $payment->fAmountRefunded && $payment->fAmountRefunded == $payment->fAmount} + (total refund) + {elseif $payment->fAmountRefunded && $payment->fAmountRefunded > 0} + (partly refund) + {/if} + + + {if $payment->oBestellung->cStatus|intval == 1} + OFFEN + {elseif $payment->oBestellung->cStatus|intval == 2} + IN BEARBEITUNG + {elseif $payment->oBestellung->cStatus|intval == 3} + BEZAHLT + {elseif $payment->oBestellung->cStatus|intval == 4} + VERSANDT + {elseif $payment->oBestellung->cStatus|intval == 5} + TEILVERSANDT + {elseif $payment->oBestellung->cStatus|intval == -1} + STORNO + {else} + n/a + {/if} + {$payment->fAmount|number_format:2:',':''}{$payment->cCurrency}{$payment->cLocale}{$payment->cMethod}{"d. M Y H:i"|date:($payment->dCreatedAt|strtotime)}
+ {if $payments|count > 900} +
Hier werden nur die letzten 1000 Ergebnisse angezeigt.
+ {/if} + + +{/if} + diff --git a/version/106/adminmenu/tpl/paymentmethods.tpl b/version/106/adminmenu/tpl/paymentmethods.tpl new file mode 100644 index 0000000..4c37e02 --- /dev/null +++ b/version/106/adminmenu/tpl/paymentmethods.tpl @@ -0,0 +1,97 @@ +

Account Status

+ + + + + + + {if $profile->review} + + + {/if} + +
Mode:{$profile->mode}Status:{$profile->status}Review:{$profile->review->status}
+ +
+ +
+
+ + +
+ + + +
+
+ + +
+
+ + +
+
+ + + reset +
+
+
+ + +{if $allMethods && $allMethods|count} + + + + + + + + + + + + {foreach from=$allMethods item=method} + + + + + + + + {/foreach} + +
BildIDNamePreiseInfos
{$method->description|utf8_decode}{$method->id}{$method->description|utf8_decode} +
    + {foreach from=$method->pricing item=price} +
  • {$price->description|utf8_decode}: {$price->fixed->value} {$price->fixed->currency} + {if $price->variable > 0.0} + + {$price->variable}% + {/if} +
  • + {/foreach} +
+
+ Min: {if $method->minimumAmount}{$method->minimumAmount->value} {$method->minimumAmount->currency}{else}n/a{/if} +
+ Max: {if $method->maximumAmount}{$method->maximumAmount->value} {$method->maximumAmount->currency}{else}n/a{/if} +
+{else} +
Es konnten keine Methoden abgerufen werden.
+{/if} \ No newline at end of file diff --git a/version/106/class/ExclusiveLock.php b/version/106/class/ExclusiveLock.php new file mode 100644 index 0000000..834e00f --- /dev/null +++ b/version/106/class/ExclusiveLock.php @@ -0,0 +1,76 @@ +key = $key; + $this->path = rtrim(realpath($path), '/'). '/' ; + if(!is_dir($path) || !is_writable($path)){ + throw new Exception("Lock Path '{$path}' doesn't exist, or is not writable!"); + } + //create a new resource or get exisitng with same key + $this->file = fopen($this->path . "$key.lockfile", 'w+'); + } + + + public function __destruct() + { + if( $this->own === true ) + $this->unlock( ); + } + + + public function lock() + { + if( !flock($this->file, LOCK_EX | LOCK_NB)) + { //failed + $key = $this->key; + error_log("ExclusiveLock::acquire_lock FAILED to acquire lock [$key]"); + return false; + } + //ftruncate($this->file, 0); // truncate file + //write something to just help debugging + //fwrite( $this->file, "Locked\n"); + fwrite( $this->file, "Locked - " . microtime(true) . "\n"); + fflush( $this->file ); + + $this->own = true; + return true; // success + } + + + public function unlock() + { + $key = $this->key; + if( $this->own === true ) + { + if( !flock($this->file, LOCK_UN) ) + { //failed + error_log("ExclusiveLock::lock FAILED to release lock [$key]"); + return false; + } + //ftruncate($this->file, 0); // truncate file + //write something to just help debugging + fwrite( $this->file, "Unlocked - " . microtime(true) . "\n"); + fflush( $this->file ); + $this->own = false; + } + else + { + error_log("ExclusiveLock::unlock called on [$key] but its not acquired by caller"); + } + return true; // success + } +} diff --git a/version/106/class/Helper.php b/version/106/class/Helper.php new file mode 100644 index 0000000..8f4772c --- /dev/null +++ b/version/106/class/Helper.php @@ -0,0 +1,311 @@ +short_url != '' ? $release->short_url : $release->full_url; + $filename = basename($release->full_url); + $tmpDir = PFAD_ROOT . PFAD_COMPILEDIR; + $pluginsDir = PFAD_ROOT . PFAD_PLUGIN; + + // 1. PRE-CHECKS + if (file_exists($pluginsDir . self::oPlugin()->cVerzeichnis . '/.git') && is_dir($pluginsDir . self::oPlugin()->cVerzeichnis . '/.git')) { + throw new Exception('Pluginordner enthält ein GIT Repository, kein Update möglich!'); + } + + if (!function_exists("curl_exec")) { + throw new Exception("cURL ist nicht verfügbar!!"); + } + if (!is_writable($tmpDir)) { + throw new Exception("Temporäres Verzeichnis_'{$tmpDir}' ist nicht beschreibbar!"); + } + if (!is_writable($pluginsDir . self::oPlugin()->cVerzeichnis)) { + throw new Exception("Plugin Verzeichnis_'" . $pluginsDir . self::oPlugin()->cVerzeichnis . "' ist nicht beschreibbar!"); + } + if (file_exists($tmpDir . $filename)) { + if (!unlink($tmpDir . $filename)) { + throw new Exception("Temporäre Datei '" . $tmpDir . $filename . "' konnte nicht gelöscht werden!"); + } + } + + // 2. DOWNLOAD + $fp = fopen($tmpDir . $filename, 'w+'); + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_TIMEOUT, 50); + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_exec($ch); + $info = curl_getinfo($ch); + curl_close($ch); + fclose($fp); + if ($info['http_code'] !== 200) { + throw new Exception("Unerwarteter Status Code '" . $info['http_code'] . "'!"); + } + if ($info['download_content_length'] <= 0) { + throw new Exception("Unerwartete Downloadgröße '" . $info['download_content_length'] . "'!"); + } + + // 3. UNZIP + require_once PFAD_ROOT . PFAD_PCLZIP . 'pclzip.lib.php'; + $zip = new PclZip($tmpDir . $filename); + $content = $zip->listContent(); + + if (!is_array($content) || !isset($content[0]['filename']) || strpos($content[0]['filename'], '.') !== false) { + throw new Exception("Das Zip-Archiv ist leider ungültig!"); + } else { + $unzipPath = PFAD_ROOT . PFAD_PLUGIN; + $res = $zip->extract(PCLZIP_OPT_PATH, $unzipPath, PCLZIP_OPT_REPLACE_NEWER); + if ($res !== 0) { + header('Location: ' . Shop::getURL() . DIRECTORY_SEPARATOR . PFAD_ADMIN . 'pluginverwaltung.php', true); + } else { + throw new Exception('Entpacken fehlgeschlagen: ' . $zip->errorCode()); + } + } + } + + /** + * @param bool $force + * @return mixed + * @throws Exception + */ + public static function getLatestRelease($force = false) + { + $lastCheck = (int)self::getSetting(__NAMESPACE__ . '_upd'); + $lastRelease = file_exists(PFAD_ROOT . PFAD_COMPILEDIR . __NAMESPACE__ . '_upd') ? file_get_contents(PFAD_ROOT . PFAD_COMPILEDIR . __NAMESPACE__ . '_upd') : false; + if ($force || !$lastCheck || !$lastRelease || ($lastCheck + 12 * 60 * 60) < time()) { + $curl = curl_init('https://api.dash.bar/release/' . __NAMESPACE__); + @curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); + @curl_setopt($curl, CURLOPT_TIMEOUT, 5); + @curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + @curl_setopt($curl, CURLOPT_HEADER, 0); + @curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + @curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); + + $data = curl_exec($curl); + $statusCode = (int)curl_getinfo($curl, CURLINFO_HTTP_CODE); + @curl_close($curl); + if ($statusCode !== 200) { + throw new Exception(__NAMESPACE__ . ': Could not fetch release info: ' . $statusCode); + } + $json = json_decode($data); + if (json_last_error() || $json->status != 'ok') { + throw new Exception(__NAMESPACE__ . ': Could not decode release info: ' . $data); + } + self::setSetting(__NAMESPACE__ . '_upd', time()); + file_put_contents(PFAD_ROOT . PFAD_COMPILEDIR . __NAMESPACE__ . '_upd', json_encode($json->data)); + return $json->data; + } else { + return json_decode($lastRelease); + } + } + + /** + * Register PSR-4 autoloader + * Licence-Check + * @return bool + */ + public static function init() + { + ini_set('xdebug.default_enable', defined('WS_XDEBUG_ENABLED')); + return self::autoload(); + } + + /** + * Sets a Plugin Setting and saves it to the DB + * + * @param $name + * @param $value + * @return int + */ + public static function setSetting($name, $value) + { + $setting = new stdClass; + $setting->kPlugin = self::oPlugin()->kPlugin; + $setting->cName = $name; + $setting->cWert = $value; + + if (array_key_exists($name, self::oPlugin()->oPluginEinstellungAssoc_arr)) { + $return = Shop::DB()->updateRow('tplugineinstellungen', ['kPlugin', 'cName'], [$setting->kPlugin, $setting->cName], $setting); + } else { + $return = Shop::DB()->insertRow('tplugineinstellungen', $setting); + } + self::oPlugin()->oPluginEinstellungAssoc_arr[$name] = $value; + self::oPlugin(true); // invalidate cache + return $return; + } + + /** + * Get Plugin Object + * + * @param bool $force disable Cache + * @return Plugin|null + */ + public static function oPlugin($force = false) + { + if ($force === true) { + self::$oPlugin = new Plugin(self::oPlugin(false)->kPlugin, true); + } else if (null === self::$oPlugin) { + self::$oPlugin = Plugin::getPluginById(__NAMESPACE__); + } + return self::$oPlugin; + } + + /** + * get a Plugin setting + * + * @param $name + * @return null|mixed + */ + public static function getSetting($name) + { + if (array_key_exists($name, self::oPlugin()->oPluginEinstellungAssoc_arr ?: [])) { + return self::oPlugin()->oPluginEinstellungAssoc_arr[$name]; + } + return null; + } + + /** + * Get Domain frpm URL_SHOP without www. + * + * @param string $url + * @return string + */ + public static function getDomain($url = URL_SHOP) + { + $matches = array(); + @preg_match("/^((http(s)?):\/\/)?(www\.)?([a-zA-Z0-9-\.]+)(\/.*)?$/i", $url, $matches); + return strtolower(isset($matches[5]) ? $matches[5] : $url); + } + + /** + * @param bool $e + * @return mixed + */ + public static function getMasterMail($e = false) + { + $settings = Shop::getSettings(array(CONF_EMAILS)); + $mail = trim($settings['emails']['email_master_absender']); + if ($e === true && $mail != '') { + $mail = base64_encode($mail); + $eMail = ""; + foreach (str_split($mail, 1) as $c) { + $eMail .= chr(ord($c) ^ 0x00100110); + } + return base64_encode($eMail); + } + return $mail; + } + + /** + * @param Exception $exc + * @param bool $trace + * @return void + */ + public static function logExc(Exception $exc, $trace = true) + { + Jtllog::writeLog(__NAMESPACE__ . ': ' . $exc->getMessage() . ($trace ? ' - ' . $exc->getTraceAsString() : '')); + } + + /** + * Checks if admin session is loaded + * + * @return bool + */ + public static function isAdminBackend() + { + return session_name() === 'eSIdAdm'; + } + + /** + * Returns kAdminmenu ID for given Title, used for Tabswitching + * + * @param $name string CustomLink Title + * @return int + */ + public static function getAdminmenu($name) + { + $kPluginAdminMenu = 0; + foreach (self::oPlugin()->oPluginAdminMenu_arr as $adminmenu) { + if (strtolower($adminmenu->cName) == strtolower($name)) { + $kPluginAdminMenu = $adminmenu->kPluginAdminMenu; + break; + } + } + return $kPluginAdminMenu; + } + + } + } + +} diff --git a/version/106/class/Model/AbstractModel.php b/version/106/class/Model/AbstractModel.php new file mode 100644 index 0000000..5638700 --- /dev/null +++ b/version/106/class/Model/AbstractModel.php @@ -0,0 +1,7 @@ +id; + $data = [ + ':kID' => $oMolliePayment->id, + ':kBestellung' => (int)$kBestellung ?: null, + ':kBestellung1' => (int)$kBestellung ?: null, + ':cMode' => $oMolliePayment->mode, + ':cStatus' => $oMolliePayment->status, + ':cStatus1' => $oMolliePayment->status, + ':cHash' => $hash, + ':fAmount' => $oMolliePayment->amount->value, + ':cOrderNumber' => $oMolliePayment->orderNumber, + ':cOrderNumber1' => $oMolliePayment->orderNumber, + ':cCurrency' => $oMolliePayment->amount->currency, + ':cMethod' => $oMolliePayment->method, + ':cMethod1' => $oMolliePayment->method, + ':cLocale' => $oMolliePayment->locale, + ':bCancelable' => $oMolliePayment->isCancelable, + ':bCancelable1' => $oMolliePayment->isCancelable, + ':cWebhookURL' => $oMolliePayment->webhookUrl, + ':cRedirectURL' => $oMolliePayment->redirectUrl, + ':cCheckoutURL' => $oMolliePayment->getCheckoutUrl(), + ':cCheckoutURL1' => $oMolliePayment->getCheckoutUrl(), + ':fAmountCaptured' => $oMolliePayment->amountCaptured ? $oMolliePayment->amountCaptured->value : null, + ':fAmountCaptured1' => $oMolliePayment->amountCaptured ? $oMolliePayment->amountCaptured->value : null, + ':fAmountRefunded' => $oMolliePayment->amountRefunded ? $oMolliePayment->amountRefunded->value : null, + ':fAmountRefunded1' => $oMolliePayment->amountRefunded ? $oMolliePayment->amountRefunded->value : null, + ':dCreatedAt' => $oMolliePayment->createdAt ? date('Y-m-d H:i:s', strtotime($oMolliePayment->createdAt)) : null, + ]; + Mollie::JTLMollie()->doLog('Payment::updateFromPayment
' . print_r([$kBestellung, $oMolliePayment], 1) . '
', $logData); + return Shop::DB()->executeQueryPrepared( + 'INSERT INTO ' . self::TABLE . ' (kID, kBestellung, cMode, cStatus, cHash, fAmount, cOrderNumber, cCurrency, cMethod, cLocale, bCancelable, cWebhookURL, cRedirectURL, cCheckoutURL, fAmountCaptured, fAmountRefunded, dCreatedAt) ' + . 'VALUES (:kID, :kBestellung, :cMode, :cStatus, :cHash, :fAmount, :cOrderNumber, :cCurrency, :cMethod, :cLocale, :bCancelable, :cWebhookURL, :cRedirectURL, IF(:cCheckoutURL IS NULL, cCheckoutURL, :cCheckoutURL1), :fAmountCaptured, :fAmountRefunded, :dCreatedAt) ' + . 'ON DUPLICATE KEY UPDATE kBestellung = :kBestellung1, cOrderNumber = :cOrderNumber1, cStatus = :cStatus1, cMethod = :cMethod1, bCancelable = :bCancelable1, fAmountCaptured = :fAmountCaptured1, fAmountRefunded = :fAmountRefunded1', + $data, + 3 + ); + } + + public static function getPayment($kBestellung) + { + $payment = Shop::DB()->executeQueryPrepared('SELECT * FROM ' . self::TABLE . ' WHERE kBestellung = :kBestellung', [':kBestellung' => $kBestellung], 1); + if ($payment && $payment->kBestellung) { + $payment->oBestellung = new Bestellung($payment->kBestellung, false); + } + return $payment; + } + + public static function getPaymentMollie($kID) + { + $payment = Shop::DB()->executeQueryPrepared('SELECT * FROM ' . self::TABLE . ' WHERE kID = :kID', [':kID' => $kID], 1); + if ($payment && $payment->kBestellung) { + $payment->oBestellung = new Bestellung($payment->kBestellung, false); + } + return $payment; + } + + /** + * @param $cHash + * @return array|int|object + */ + public static function getPaymentHash($cHash) + { + $payment = Shop::DB()->executeQueryPrepared('SELECT * FROM ' . self::TABLE . ' WHERE cHash = :cHash', [':cHash' => $cHash], 1); + if ($payment && $payment->kBestellung) { + $payment->oBestellung = new Bestellung($payment->kBestellung, false); + } + return $payment; + } +} diff --git a/version/106/class/Mollie.php b/version/106/class/Mollie.php new file mode 100644 index 0000000..9e5520e --- /dev/null +++ b/version/106/class/Mollie.php @@ -0,0 +1,508 @@ +getValue(CONF_KAUFABWICKLUNG, 'bestellabschluss_abschlussseite'); + + $bestellid = Shop::DB()->select("tbestellid ", 'kBestellung', (int)$kBestellung); + $url = Shop::getURL() . '/bestellabschluss.php?i=' . $bestellid->cId; + + + if ($mode == 'S' || !$bestellid) { // Statusseite + $bestellstatus = Shop::DB()->select('tbestellstatus', 'kBestellung', (int)$kBestellung); + $url = Shop::getURL() . '/status.php?uid=' . $bestellstatus->cUID; + } + + if ($redirect) { + if (!headers_sent()) { + header('Location: ' . $url); + } + echo "redirect ..."; + exit(); + } + return $url; + } + + /** + * @param Order $order + * @param $kBestellung + * @param bool $newStatus + * @return array + * @throws Exception + */ + public static function getShipmentOptions(Order $order, $kBestellung, $newStatus = false) + { + if (!$order || !$kBestellung) { + throw new Exception('Mollie::getShipmentOptions: order and kBestellung are required!'); + } + + $oBestellung = new Bestellung($kBestellung, true); + if ($newStatus === false) { + $newStatus = $oBestellung->cStatus; + } + $options = []; + + // Tracking Data + if ($oBestellung->cTracking) { + $tracking = new stdClass(); + $tracking->carrier = $oBestellung->cVersandartName; + $tracking->url = $oBestellung->cTrackingURL; + $tracking->code = $oBestellung->cTracking; + $options['tracking'] = $tracking; + } + + switch ((int)$newStatus) { + case BESTELLUNG_STATUS_VERSANDT: + Mollie::JTLMollie()->doLog('181_sync: Bestellung versandt', '#' . $oBestellung->kBestellung . '§' . $oBestellung->cBestellNr, LOGLEVEL_DEBUG); + $options['lines'] = []; + break; + case BESTELLUNG_STATUS_TEILVERSANDT: + $lines = []; + foreach ($order->lines as $i => $line) { + if (($quantity = Mollie::getBestellPosSent($line->sku, $oBestellung)) !== false && ($quantity - $line->quantityShipped) > 0) { + $x = $quantity - $line->quantityShipped; + $lines[] = (object)[ + 'id' => $line->id, + 'quantity' => $x, + 'amount' => (object)[ + 'currency' => $line->totalAmount->currency, + 'value' => number_format($x * $line->unitPrice->value, 2), + ], + ]; + } + } + Mollie::JTLMollie()->doLog('181_sync: Bestellung teilversandt', '#' . $oBestellung->kBestellung . '§' . $oBestellung->cBestellNr, LOGLEVEL_DEBUG); + if (count($lines)) { + $options['lines'] = $lines; + } + break; + case BESTELLUNG_STATUS_STORNO: + Mollie::JTLMollie()->doLog('181_sync: Bestellung storniert', '#' . $oBestellung->kBestellung . '§' . $oBestellung->cBestellNr, LOGLEVEL_DEBUG); + $options = null; + break; + default: + Mollie::JTLMollie()->doLog('181_sync: Bestellungstatus unbekannt: ' . $newStatus . '/' . $oBestellung->cStatus, '#' . $oBestellung->kBestellung . '§' . $oBestellung->cBestellNr, LOGLEVEL_DEBUG); + } + + return $options; + } + + /** + * @return JTLMollie + * @throws Exception + */ + public static function JTLMollie() + { + if (self::$_jtlmollie === null) { + $pza = Shop::DB()->select('tpluginzahlungsartklasse', 'cClassName', 'JTLMollie'); + if (!$pza) { + throw new Exception("Mollie Zahlungsart nicht in DB gefunden!"); + } + require_once __DIR__ . '/../paymentmethod/JTLMollie.php'; + self::$_jtlmollie = new JTLMollie($pza->cModulId); + } + return self::$_jtlmollie; + } + + /** + * Returns amount of sent items for SKU + * @param $sku + * @param Bestellung $oBestellung + * @return float|int + * @throws Exception + */ + public static function getBestellPosSent($sku, Bestellung $oBestellung) + { + if ($sku === null) { + return 1; + } + /** @var WarenkorbPos $oPosition */ + foreach ($oBestellung->Positionen as $oPosition) { + if ($oPosition->cArtNr === $sku) { + $sent = 0; + /** @var Lieferschein $oLieferschein */ + foreach ($oBestellung->oLieferschein_arr as $oLieferschein) { + /** @var Lieferscheinpos $oLieferscheinPos */ + foreach ($oLieferschein->oLieferscheinPos_arr as $oLieferscheinPos) { + if ($oLieferscheinPos->getBestellPos() == $oPosition->kBestellpos) { + $sent += $oLieferscheinPos->getAnzahl(); + } + } + } + return $sent; + } + } + return false; + } + + /** + * @param Order $order + * @param null $kBestellung + * @return bool + * @throws Exception + */ + public static function handleOrder(Order $order, $kBestellung) + { + $logData = '$' . $order->id . '#' . $kBestellung . "§" . $order->orderNumber; + + $oBestellung = new Bestellung($kBestellung); + if ($oBestellung->kBestellung) { + + Shop::DB()->executeQueryPrepared("INSERT INTO tbestellattribut (kBestellung, cName, cValue) VALUES (:kBestellung, 'mollie_oid', :mollieId1) ON DUPLICATE KEY UPDATE cValue = :mollieId2;", [ + ':kBestellung' => $kBestellung, + ':mollieId1' => $order->id, + ':mollieId2' => $order->id, + ], 3); + + Shop::DB()->executeQueryPrepared("INSERT INTO tbestellattribut (kBestellung, cName, cValue) VALUES (:kBestellung, 'mollie_cBestellNr', :orderId1) ON DUPLICATE KEY UPDATE cValue = :orderId2;", [ + ':kBestellung' => $kBestellung, + ':orderId1' => $oBestellung->cBestellNr, + ':orderId2' => $oBestellung->cBestellNr, + ], 3); + + $mPayment = null; + if ($payments = $order->payments()) { + /** @var \Mollie\Api\Resources\Payment $payment */ + foreach ($payments as $payment) { + if (in_array($payment->status, [PaymentStatus::STATUS_AUTHORIZED, PaymentStatus::STATUS_PAID])) { + $mPayment = $payment; + } + } + } + if ($mPayment) { + Shop::DB()->executeQueryPrepared("INSERT INTO tbestellattribut (kBestellung, cName, cValue) VALUES (:kBestellung, 'mollie_tid', :mollieId1) ON DUPLICATE KEY UPDATE cValue = :mollieId2;", [ + ':kBestellung' => $kBestellung, + ':mollieId1' => $mPayment->id, + ':mollieId2' => $mPayment->id, + ], 3); + } + + try { + // Try to change the orderNumber + if ($order->orderNumber !== $oBestellung->cBestellNr) { + JTLMollie::API()->performHttpCall("PATCH", sprintf('orders/%s', $order->id), json_encode(['orderNumber' => $oBestellung->cBestellNr])); + } + } catch (Exception $e) { + self::JTLMollie()->doLog('Mollie::handleOrder: ' . $e->getMessage(), $logData); + } + + $_payment = self::getLastPayment($order); + + if ($_payment && $_payment->description !== $oBestellung->cBestellNr) { + JTLMollie::API()->performHttpCall('PATCH', sprintf('payments/%s', $_payment->id), json_encode(['description' => $oBestellung->cBestellNr])); + } + + + $order->orderNumber = $oBestellung->cBestellNr; + Payment::updateFromPayment($order, $kBestellung); + + $oIncomingPayment = Shop::DB()->executeQueryPrepared("SELECT * FROM tzahlungseingang WHERE kBestellung = :kBestellung", [':kBestellung' => $oBestellung->kBestellung], 1); + if (!$oIncomingPayment) { + $oIncomingPayment = new stdClass(); + } + + // 2. Check PaymentStatus + switch ($order->status) { + case OrderStatus::STATUS_PAID: + case OrderStatus::STATUS_COMPLETED: + case OrderStatus::STATUS_AUTHORIZED: + + $cHinweis = $order->id; + if ($mPayment) { + $cHinweis .= ' / ' . $mPayment->id; + } + if (Helper::getSetting('wawiPaymentID') === 'ord') { + $cHinweis = $order->id; + } elseif ($mPayment && Helper::getSetting('wawiPaymentID') === 'tr') { + $cHinweis = $mPayment->id; + } + + $oIncomingPayment->fBetrag = $order->amount->value; + $oIncomingPayment->cISO = $order->amount->currency; + $oIncomingPayment->cHinweis = $cHinweis; + Mollie::JTLMollie()->addIncomingPayment($oBestellung, $oIncomingPayment); + Mollie::JTLMollie()->setOrderStatusToPaid($oBestellung); + Mollie::JTLMollie()->doLog('Mollie::handleOrder/PaymentStatus: ' . $order->status . ' => Zahlungseingang (' . $order->amount->value . ')', $logData, LOGLEVEL_DEBUG); + break; + case OrderStatus::STATUS_SHIPPING: + case OrderStatus::STATUS_PENDING: + Mollie::JTLMollie()->setOrderStatusToPaid($oBestellung); + Mollie::JTLMollie()->doLog('Mollie::handleOrder/PaymentStatus: ' . $order->status . ' => Bestellung bezahlt, KEIN Zahlungseingang', $logData, LOGLEVEL_NOTICE); + break; + case OrderStatus::STATUS_CANCELED: + case OrderStatus::STATUS_EXPIRED: + Mollie::JTLMollie()->doLog('Mollie::handleOrder/PaymentStatus: ' . $order->status, $logData, LOGLEVEL_ERROR); + break; + } + return true; + } + return false; + } + + /** + * @param Order $order + * @return \Mollie\Api\Resources\Payment|null + */ + public static function getLastPayment(Order $order) + { + $payment = null; + if ($order->payments()) { + /** @var \Mollie\Api\Resources\Payment $p */ + foreach ($order->payments() as $p) { + if (!$payment) { + $payment = $p; + continue; + } + if (strtotime($p->createdAt) > strtotime($payment->createdAt)) { + $payment = $p; + } + } + } + return $payment; + } + + public static function getLocales() + { + $locales = ['en_US', + 'nl_NL', + 'nl_BE', + 'fr_FR', + 'fr_BE', + 'de_DE', + 'de_AT', + 'de_CH', + 'es_ES', + 'ca_ES', + 'pt_PT', + 'it_IT', + 'nb_NO', + 'sv_SE', + 'fi_FI', + 'da_DK', + 'is_IS', + 'hu_HU', + 'pl_PL', + 'lv_LV', + 'lt_LT',]; + + $laender = []; + $shopLaender = Shop::DB()->executeQuery("SELECT cLaender FROM tversandart", 2); + foreach ($shopLaender as $sL) { + $laender = array_merge(explode(' ', $sL->cLaender)); + } + $laender = array_unique($laender); + + $result = []; + $shopSprachen = Shop::DB()->executeQuery("SELECT * FROM tsprache", 2); + foreach ($shopSprachen as $sS) { + foreach ($laender as $land) { + $result[] = JTLMollie::getLocale($sS->cISO, $land); + } + } + return array_unique($result); + } + + public static function getCurrencies() + { + $currencies = ['AED' => 'AED - United Arab Emirates dirham', + 'AFN' => 'AFN - Afghan afghani', + 'ALL' => 'ALL - Albanian lek', + 'AMD' => 'AMD - Armenian dram', + 'ANG' => 'ANG - Netherlands Antillean guilder', + 'AOA' => 'AOA - Angolan kwanza', + 'ARS' => 'ARS - Argentine peso', + 'AUD' => 'AUD - Australian dollar', + 'AWG' => 'AWG - Aruban florin', + 'AZN' => 'AZN - Azerbaijani manat', + 'BAM' => 'BAM - Bosnia and Herzegovina convertible mark', + 'BBD' => 'BBD - Barbados dollar', + 'BDT' => 'BDT - Bangladeshi taka', + 'BGN' => 'BGN - Bulgarian lev', + 'BHD' => 'BHD - Bahraini dinar', + 'BIF' => 'BIF - Burundian franc', + 'BMD' => 'BMD - Bermudian dollar', + 'BND' => 'BND - Brunei dollar', + 'BOB' => 'BOB - Boliviano', + 'BRL' => 'BRL - Brazilian real', + 'BSD' => 'BSD - Bahamian dollar', + 'BTN' => 'BTN - Bhutanese ngultrum', + 'BWP' => 'BWP - Botswana pula', + 'BYN' => 'BYN - Belarusian ruble', + 'BZD' => 'BZD - Belize dollar', + 'CAD' => 'CAD - Canadian dollar', + 'CDF' => 'CDF - Congolese franc', + 'CHF' => 'CHF - Swiss franc', + 'CLP' => 'CLP - Chilean peso', + 'CNY' => 'CNY - Renminbi (Chinese) yuan', + 'COP' => 'COP - Colombian peso', + 'COU' => 'COU - Unidad de Valor Real (UVR)', + 'CRC' => 'CRC - Costa Rican colon', + 'CUC' => 'CUC - Cuban convertible peso', + 'CUP' => 'CUP - Cuban peso', + 'CVE' => 'CVE - Cape Verde escudo', + 'CZK' => 'CZK - Czech koruna', + 'DJF' => 'DJF - Djiboutian franc', + 'DKK' => 'DKK - Danish krone', + 'DOP' => 'DOP - Dominican peso', + 'DZD' => 'DZD - Algerian dinar', + 'EGP' => 'EGP - Egyptian pound', + 'ERN' => 'ERN - Eritrean nakfa', + 'ETB' => 'ETB - Ethiopian birr', + 'EUR' => 'EUR - Euro', + 'FJD' => 'FJD - Fiji dollar', + 'FKP' => 'FKP - Falkland Islands pound', + 'GBP' => 'GBP - Pound sterling', + 'GEL' => 'GEL - Georgian lari', + 'GHS' => 'GHS - Ghanaian cedi', + 'GIP' => 'GIP - Gibraltar pound', + 'GMD' => 'GMD - Gambian dalasi', + 'GNF' => 'GNF - Guinean franc', + 'GTQ' => 'GTQ - Guatemalan quetzal', + 'GYD' => 'GYD - Guyanese dollar', + 'HKD' => 'HKD - Hong Kong dollar', + 'HNL' => 'HNL - Honduran lempira', + 'HRK' => 'HRK - Croatian kuna', + 'HTG' => 'HTG - Haitian gourde', + 'HUF' => 'HUF - Hungarian forint', + 'IDR' => 'IDR - Indonesian rupiah', + 'ILS' => 'ILS - Israeli new shekel', + 'INR' => 'INR - Indian rupee', + 'IQD' => 'IQD - Iraqi dinar', + 'IRR' => 'IRR - Iranian rial', + 'ISK' => 'ISK - Icelandic króna', + 'JMD' => 'JMD - Jamaican dollar', + 'JOD' => 'JOD - Jordanian dinar', + 'JPY' => 'JPY - Japanese yen', + 'KES' => 'KES - Kenyan shilling', + 'KGS' => 'KGS - Kyrgyzstani som', + 'KHR' => 'KHR - Cambodian riel', + 'KMF' => 'KMF - Comoro franc', + 'KPW' => 'KPW - North Korean won', + 'KRW' => 'KRW - South Korean won', + 'KWD' => 'KWD - Kuwaiti dinar', + 'KYD' => 'KYD - Cayman Islands dollar', + 'KZT' => 'KZT - Kazakhstani tenge', + 'LAK' => 'LAK - Lao kip', + 'LBP' => 'LBP - Lebanese pound', + 'LKR' => 'LKR - Sri Lankan rupee', + 'LRD' => 'LRD - Liberian dollar', + 'LSL' => 'LSL - Lesotho loti', + 'LYD' => 'LYD - Libyan dinar', + 'MAD' => 'MAD - Moroccan dirham', + 'MDL' => 'MDL - Moldovan leu', + 'MGA' => 'MGA - Malagasy ariary', + 'MKD' => 'MKD - Macedonian denar', + 'MMK' => 'MMK - Myanmar kyat', + 'MNT' => 'MNT - Mongolian tögrög', + 'MOP' => 'MOP - Macanese pataca', + 'MRU' => 'MRU - Mauritanian ouguiya', + 'MUR' => 'MUR - Mauritian rupee', + 'MVR' => 'MVR - Maldivian rufiyaa', + 'MWK' => 'MWK - Malawian kwacha', + 'MXN' => 'MXN - Mexican peso', + 'MXV' => 'MXV - Mexican Unidad de Inversion (UDI)', + 'MYR' => 'MYR - Malaysian ringgit', + 'MZN' => 'MZN - Mozambican metical', + 'NAD' => 'NAD - Namibian dollar', + 'NGN' => 'NGN - Nigerian naira', + 'NIO' => 'NIO - Nicaraguan córdoba', + 'NOK' => 'NOK - Norwegian krone', + 'NPR' => 'NPR - Nepalese rupee', + 'NZD' => 'NZD - New Zealand dollar', + 'OMR' => 'OMR - Omani rial', + 'PAB' => 'PAB - Panamanian balboa', + 'PEN' => 'PEN - Peruvian sol', + 'PGK' => 'PGK - Papua New Guinean kina', + 'PHP' => 'PHP - Philippine peso', + 'PKR' => 'PKR - Pakistani rupee', + 'PLN' => 'PLN - Polish z?oty', + 'PYG' => 'PYG - Paraguayan guaraní', + 'QAR' => 'QAR - Qatari riyal', + 'RON' => 'RON - Romanian leu', + 'RSD' => 'RSD - Serbian dinar', + 'RUB' => 'RUB - Russian ruble', + 'RWF' => 'RWF - Rwandan franc', + 'SAR' => 'SAR - Saudi riyal', + 'SBD' => 'SBD - Solomon Islands dollar', + 'SCR' => 'SCR - Seychelles rupee', + 'SDG' => 'SDG - Sudanese pound', + 'SEK' => 'SEK - Swedish krona/kronor', + 'SGD' => 'SGD - Singapore dollar', + 'SHP' => 'SHP - Saint Helena pound', + 'SLL' => 'SLL - Sierra Leonean leone', + 'SOS' => 'SOS - Somali shilling', + 'SRD' => 'SRD - Surinamese dollar', + 'SSP' => 'SSP - South Sudanese pound', + 'STN' => 'STN - São Tomé and Príncipe dobra', + 'SVC' => 'SVC - Salvadoran colón', + 'SYP' => 'SYP - Syrian pound', + 'SZL' => 'SZL - Swazi lilangeni', + 'THB' => 'THB - Thai baht', + 'TJS' => 'TJS - Tajikistani somoni', + 'TMT' => 'TMT - Turkmenistan manat', + 'TND' => 'TND - Tunisian dinar', + 'TOP' => 'TOP - Tongan pa?anga', + 'TRY' => 'TRY - Turkish lira', + 'TTD' => 'TTD - Trinidad and Tobago dollar', + 'TWD' => 'TWD - New Taiwan dollar', + 'TZS' => 'TZS - Tanzanian shilling', + 'UAH' => 'UAH - Ukrainian hryvnia', + 'UGX' => 'UGX - Ugandan shilling', + 'USD' => 'USD - United States dollar', + 'UYI' => 'UYI - Uruguay Peso en Unidades Indexadas', + 'UYU' => 'UYU - Uruguayan peso', + 'UYW' => 'UYW - Unidad previsional', + 'UZS' => 'UZS - Uzbekistan som', + 'VES' => 'VES - Venezuelan bolívar soberano', + 'VND' => 'VND - Vietnamese ??ng', + 'VUV' => 'VUV - Vanuatu vatu', + 'WST' => 'WST - Samoan tala', + 'YER' => 'YER - Yemeni rial', + 'ZAR' => 'ZAR - South African rand', + 'ZMW' => 'ZMW - Zambian kwacha', + 'ZWL' => 'ZWL - Zimbabwean dollar']; + + $shopCurrencies = Shop::DB()->executeQuery("SELECT * FROM twaehrung", 2); + + $result = []; + + foreach ($shopCurrencies as $sC) { + if (array_key_exists($sC->cISO, $currencies)) { + $result[$sC->cISO] = $currencies[$sC->cISO]; + } + } + + return $result; + } +} diff --git a/version/106/frontend/131_globalinclude.php b/version/106/frontend/131_globalinclude.php new file mode 100644 index 0000000..ce69519 --- /dev/null +++ b/version/106/frontend/131_globalinclude.php @@ -0,0 +1,119 @@ +cNotifyID; + + if (!(int)$oZahlungSession->kBestellung && $oZahlungSession->cNotifyID) { + Mollie::JTLMollie()->doLog("Hook 131: Bestellung noch nicht finalisiert ({$oZahlungSession->cNotifyID})", $logData, LOGLEVEL_DEBUG); + // Bestellung noch nicht finalisiert + $mOrder = JTLMollie::API()->orders->get($oZahlungSession->cNotifyID, ['embed' => 'payments']); + if ($mOrder && $mOrder->id === $oZahlungSession->cNotifyID) { + + $lock = new \ws_mollie\ExclusiveLock('mollie_' . $mOrder->id, PFAD_ROOT . PFAD_COMPILEDIR); + $logged = false; + $maxWait = 300; + while (!$lock->lock() && $maxWait > 0) { + if (!$logged) { + Mollie::JTLMollie()->doLog("Hook 131: Order currently locked ({$oZahlungSession->cNotifyID})", $logData, LOGLEVEL_DEBUG); + $logged = microtime(true); + } + usleep(100000); + $maxWait--; + } + + if ($logged) { + Mollie::JTLMollie()->doLog("Hook 131: Order unlocked (after " . round(microtime(true) - $logged, 2) . "s - maxWait left: {$maxWait})", $logData, LOGLEVEL_DEBUG); + } else { + Mollie::JTLMollie()->doLog("Hook 131: Order locked - maxWait left: {$maxWait})", $logData, LOGLEVEL_DEBUG); + } + + $oZahlungSession = JTLMollie::getZahlungSession($_REQUEST['mollie']); + if ((int)$oZahlungSession->kBestellung) { + Mollie::JTLMollie()->doLog("Hook 131: Order finalized already ({$oZahlungSession->kBestellung}) => redirect", $logData, LOGLEVEL_DEBUG); + return Mollie::getOrderCompletedRedirect($oZahlungSession->kBestellung, true); + } + + Mollie::JTLMollie()->doLog("Hook 131: Order {$mOrder->id} - {$mOrder->status}
" . print_r($mOrder, 1) . "
", $logData, LOGLEVEL_DEBUG); + if (!in_array($mOrder->status, [OrderStatus::STATUS_EXPIRED, OrderStatus::STATUS_CANCELED])) { + + $payment = Mollie::getLastPayment($mOrder); + if (in_array($payment->status, [PaymentStatus::STATUS_AUTHORIZED, PaymentStatus::STATUS_PAID, PaymentStatus::STATUS_PENDING])) { + + if (session_id() !== $oZahlungSession->cSID) { + Mollie::JTLMollie()->doLog("Hook 131: Switch to PaymentSession
" . print_r([session_id(), $oZahlungSession], 1) . "
", $logData, LOGLEVEL_DEBUG); + session_destroy(); + session_id($oZahlungSession->cSID); + $session = Session::getInstance(true, true); + } else { + Mollie::JTLMollie()->doLog("Hook 131: Already in PaymentSession
" . print_r([session_id(), $oZahlungSession], 1) . "
", $logData, LOGLEVEL_DEBUG); + $session = Session::getInstance(false, false); + } + + require_once PFAD_ROOT . 'includes/bestellabschluss_inc.php'; + require_once PFAD_ROOT . 'includes/mailTools.php'; + + $order = fakeBestellung(); + $order = finalisiereBestellung(); + $session->cleanUp(); + $logData .= '#' . $order->kBestellung . '§' . $order->cBestellNr; + Mollie::JTLMollie()->doLog("Hook 131: Bestellung finalisiert
" . print_r([$order->kBestellung, $order->cBestellNr], 1) . "
", $logData, LOGLEVEL_DEBUG); + + if ($order->kBestellung > 0) { + Mollie::JTLMollie()->doLog("Hook 131: Finalisierung erfolgreich, kBestellung: {$order->kBestellung} / {$order->cBestellNr}", $logData, LOGLEVEL_DEBUG); + $oZahlungSession->nBezahlt = 1; + $oZahlungSession->dZeitBezahlt = 'now()'; + $oZahlungSession->kBestellung = (int)$order->kBestellung; + $oZahlungSession->dNotify = strtotime($oZahlungSession->dNotify > 0) ? $oZahlungSession->dNotify : date("Y-m-d H:i:s"); + Shop::DB()->update('tzahlungsession', 'cZahlungsID', $oZahlungSession->cZahlungsID, $oZahlungSession); + Mollie::handleOrder($mOrder, $order->kBestellung); + return Mollie::getOrderCompletedRedirect($order->kBestellung, true); + } else { + Mollie::JTLMollie()->doLog("Hook 131: Fionalisierung fehlgeschlagen
" . print_r($order, 1) . "
", $logData, LOGLEVEL_ERROR); + } + } else { + Mollie::JTLMollie()->doLog("Hook 131: Invalid PaymentStatus: {$payment->status} for {$payment->id} ", $logData, LOGLEVEL_ERROR); + header('Location: ' . Shop::getURL() . '/bestellvorgang.php?editZahlungsart=1&mollieStatus=' . $payment->status); + exit(); + } + + } else { + Mollie::JTLMollie()->doLog("Hook 131: Invalid OrderStatus: {$mOrder->status} for {$mOrder->id} ", $logData, LOGLEVEL_ERROR); + header('Location: ' . Shop::getURL() . '/bestellvorgang.php?editZahlungsart=1&mollieStatus=' . $mOrder->status); + exit(); + } + } else { + Mollie::JTLMollie()->doLog("Hook 131: Could not get Order for {$oZahlungSession->cNotifyID}", $logData, LOGLEVEL_ERROR); + header('Location: ' . Shop::getURL() . '/bestellvorgang.php?editZahlungsart=1'); + exit(); + } + } else { + Mollie::JTLMollie()->doLog("Hook 131: already finalized => redirect / kBestellung:{$oZahlungSession->kBestellung} && cNotifyID:{$oZahlungSession->cNotifyID}", $logData, LOGLEVEL_NOTICE); + } + return Mollie::getOrderCompletedRedirect((int)$oZahlungSession->kBestellung, true); + } + } + ob_end_flush(); +} catch (Exception $e) { + Helper::logExc($e); +} + diff --git a/version/106/frontend/140_smarty.php b/version/106/frontend/140_smarty.php new file mode 100644 index 0000000..0213117 --- /dev/null +++ b/version/106/frontend/140_smarty.php @@ -0,0 +1,71 @@ +oPluginSprachvariableAssoc_arr['error_' . $status]; + pq('#fieldset-payment')->prepend('
' . $text . '
'); + } + + $applePayId = "kPlugin_".Helper::oPlugin()->kPlugin."_mollieapplepay"; +pq('body')->append(<< +// +HTML +); + + switch (Helper::getSetting('load_styles')) { + case 'Y': + $selector = '#fieldset-payment [id*="_mollie"]'; + $border = ""; + break; + case 'A': + $selector = '#fieldset-payment'; + $border = "border-bottom: 1px solid #ccc;"; + break; + case 'N': + default: + return; + } + + $lh = "30px"; + if (Helper::getSetting('paymentmethod_sync') === 'size2x') { + $lh = "40px"; + } + + pq('head')->append( + << + /* MOLLIE CHECKOUT STYLES*/ + #fieldset-payment .form-group > div:hover, #checkout-shipping-payment .form-group > div:hover { + background-color: #eee; + color: black; + } + + {$selector} label > span { + line-height: {$lh}; + } + {$selector} label { + {$border} + } + {$selector} label img { + float: right; + } + +HTML + ); +} catch (Exception $e) { + Helper::logExc($e); +} diff --git a/version/106/frontend/144_notify.php b/version/106/frontend/144_notify.php new file mode 100644 index 0000000..65e24e3 --- /dev/null +++ b/version/106/frontend/144_notify.php @@ -0,0 +1,57 @@ + Update Payment, stop skript + * - ELSE weiter mit der notify.php + */ + + +use ws_mollie\Helper; +use ws_mollie\Mollie; + +try { + + require_once __DIR__ . '/../class/Helper.php'; + + Helper::init(); + + require_once __DIR__ . '/../paymentmethod/JTLMollie.php'; + + $orderId = array_key_exists('id', $_REQUEST) ? $_REQUEST['id'] : false; + if (!$orderId) { + // NOT A MOLLIE NOTIFICATION! + return; + } + $sh = array_key_exists('sh', $_REQUEST) ? $_REQUEST['sh'] : false; + if (!$sh) { + // NO SESSION HASH GIVEN! + return; + } + + $logData = '$' . $orderId; + + if ($oZahlungSession = JTLMollie::getZahlungSession(md5(trim($sh, '_')))) { + if ((int)$oZahlungSession->kBestellung <= 0) { + // Bestellung noch nicht abgeschlossen, weiter mit standard + Mollie::JTLMollie()->doLog("Hook 144: orderId open, finalize with shop standard: {$orderId} / {$oZahlungSession->cZahlungsID}", $logData, LOGLEVEL_NOTICE); + return; + } + + if (trim($oZahlungSession->cNotifyID) === trim($orderId)) { + $logData = '$' . $oZahlungSession->cNotifyID; + Mollie::JTLMollie()->doLog("Hook 144: order finalized already => handleNotification", $logData, LOGLEVEL_NOTICE); + // Bestellung bereits finalisiert => evtl. Statusänderung + $oOrder = JTLMollie::API()->orders->get($orderId, ['embed' => 'payments']); + Mollie::handleOrder($oOrder, (int)$oZahlungSession->kBestellung); + exit(); + } else { + Mollie::JTLMollie()->doLog("Hook 144: orderId invalid: {$orderId} / {$oZahlungSession->cNotifyID}", $logData, LOGLEVEL_ERROR); + } + } else { + Mollie::JTLMollie()->doLog("Hook 144: couldn't load ZahlungSession => {$sh}", $logData, LOGLEVEL_DEBUG); + } + +} catch (Exception $e) { + Helper::logExc($e); +} diff --git a/version/106/frontend/181_sync.php b/version/106/frontend/181_sync.php new file mode 100644 index 0000000..86c610c --- /dev/null +++ b/version/106/frontend/181_sync.php @@ -0,0 +1,43 @@ +kBestellung && $payment = Payment::getPayment($oBestellung->kBestellung)) { + $logData = '#' . $payment->kBestellung . '$' . $payment->kID . "§" . $oBestellung->cBestellNr; + Mollie::JTLMollie()->doLog("WAWI Abgleich: HOOK_BESTELLUNGEN_XML_BESTELLSTATUS
" . print_r($args_arr, 1) . "
", $logData, LOGLEVEL_DEBUG); + try { + $order = JTLMollie::API()->orders->get($payment->kID); + //$order->orderNumber = $oBestellung->cBestellNr; + Mollie::handleOrder($order, $oBestellung->kBestellung); + if ($order->isCreated() || $order->isPaid() || $order->isAuthorized() || $order->isShipping() || $order->isPending()) { + $options = Mollie::getShipmentOptions($order, $oBestellung->kBestellung, $status); + if ($options && array_key_exists('lines', $options) && is_array($options['lines'])) { + require_once __DIR__ . '/../paymentmethod/JTLMollie.php'; + $shipment = JTLMollie::API()->shipments->createFor($order, $options); + Mollie::JTLMollie()->doLog('Shipment created
' . print_r(['options' => $options, 'shipment' => $shipment], 1) . '
', $logData, LOGLEVEL_NOTICE); + } elseif ((int)$status !== BESTELLUNG_STATUS_BEZAHLT) { + Mollie::JTLMollie()->doLog('181_sync: options don\'t contain lines
' . print_r([$order, $options], 1) . '
', $logData, LOGLEVEL_ERROR); + } + } + } catch (Exception $e) { + Mollie::JTLMollie()->doLog('Fehler: ' . $e->getMessage() . '
' . print_r($e->getTrace(), 1) . '
', $logData, LOGLEVEL_ERROR); + } + } + + //} +} catch (Exception $e) { + Helper::logExc($e); +} diff --git a/version/106/frontend/img/trust_eng.png b/version/106/frontend/img/trust_eng.png new file mode 100644 index 0000000..3d01773 Binary files /dev/null and b/version/106/frontend/img/trust_eng.png differ diff --git a/version/106/frontend/img/trust_fre.png b/version/106/frontend/img/trust_fre.png new file mode 100644 index 0000000..ed1a2f6 Binary files /dev/null and b/version/106/frontend/img/trust_fre.png differ diff --git a/version/106/frontend/img/trust_ger.png b/version/106/frontend/img/trust_ger.png new file mode 100644 index 0000000..e42821b Binary files /dev/null and b/version/106/frontend/img/trust_ger.png differ diff --git a/version/106/paymentmethod/JTLMollie.php b/version/106/paymentmethod/JTLMollie.php new file mode 100644 index 0000000..6d69cdc --- /dev/null +++ b/version/106/paymentmethod/JTLMollie.php @@ -0,0 +1,656 @@ + (int)$order->kBestellung, + 'cZahlungsanbieter' => empty($order->cZahlungsartName) ? $this->name : $order->cZahlungsartName, + 'fBetrag' => 0, + 'fZahlungsgebuehr' => 0, + 'cISO' => array_key_exists('Waehrung', $_SESSION) ? $_SESSION['Waehrung']->cISO : $payment->cISO, + 'cEmpfaenger' => '', + 'cZahler' => '', + 'dZeit' => 'now()', + 'cHinweis' => '', + 'cAbgeholt' => 'N' + ], (array)$payment); + + $logData = '#' . $order->kBestellung; + + if (isset($model->kZahlungseingang) && $model->kZahlungseingang > 0) { + Mollie::JTLMollie()->doLog('JTLMollie::addIncomingPayment (update)
' . print_r([$model, $payment], 1) . '
', $logData); + Shop::DB()->update('tzahlungseingang', 'kZahlungseingang', $model->kZahlungseingang, $model); + } else { + Mollie::JTLMollie()->doLog('JTLMollie::addIncomingPayment (create)
' . print_r([$model, $payment], 1) . '
', $logData); + Shop::DB()->insert('tzahlungseingang', $model); + } + + return $this; + } + + /** + * @param string $msg + * @param null $data + * @param int $level + * @return $this + */ + public function doLog($msg, $data = null, $level = LOGLEVEL_NOTICE) + { + //ZahlungsLog::add($this->moduleID, $msg, $data, $level); + ZahlungsLog::add($this->moduleID, "[" . microtime(true) . " - " . $_SERVER['PHP_SELF'] . "] " . $msg, $data, $level); + + return $this; + } + + /** + * @param Bestellung $order + * @return PaymentMethod|void + */ + public function setOrderStatusToPaid($order) + { + // If paid already, do nothing + if ((int)$order->cStatus >= BESTELLUNG_STATUS_BEZAHLT) { + return $this; + } + parent::setOrderStatusToPaid($order); + } + + /** + * Prepares everything so that the Customer can start the Payment Process. + * Tells Template Engine. + * + * @param Bestellung $order + * @return bool|string + */ + public function preparePaymentProcess($order) + { + $logData = '#' . $order->kBestellung . "§" . $order->cBestellNr; + + $payable = (float)$order->fGesamtsumme > 0; + + try { + if ($order->kBestellung) { + if ($payable) { + $payment = Payment::getPayment($order->kBestellung); + $oMolliePayment = self::API()->orders->get($payment->kID, ['embed' => 'payments']); + Mollie::handleOrder($oMolliePayment, $order->kBestellung); + if ($payment && in_array($payment->cStatus, [OrderStatus::STATUS_CREATED]) && $payment->cCheckoutURL) { + $logData .= '$' . $payment->kID; + if (!$this->duringCheckout) { + Session::getInstance()->cleanUp(); + } + header('Location: ' . $payment->cCheckoutURL); + echo "redirect to payment ..."; + exit(); + } + } else { + return Mollie::getOrderCompletedRedirect($order->kBestellung, true); + } + } + } catch (Exception $e) { + $this->doLog("Get Payment Error: " . $e->getMessage() . ". Create new ORDER...", $logData, LOGLEVEL_ERROR); + } + + + try { + + + if (!$payable) { + $bestellung = finalisiereBestellung(); + if ($bestellung && (int)$bestellung->kBestellung > 0) { + return Mollie::getOrderCompletedRedirect($bestellung->kBestellung, true); + } + header('Location: ' . Shop::getURL() . '/bestellvorgang.php?editZahlungsart=1&mollieStatus=failed'); + echo "redirect..."; + exit(); + } + + if (!array_key_exists('oMolliePayment', $_SESSION) || !($_SESSION['oMolliePayment'] instanceof \Mollie\Api\Resources\Order)) { + $hash = $this->generateHash($order); + //$_SESSION['cMollieHash'] = $hash; + $orderData = $this->getOrderData($order, $hash); + $oMolliePayment = self::API()->orders->create($orderData); + $this->updateHash($hash, $oMolliePayment->id); + $_SESSION['oMolliePayment'] = $oMolliePayment; + } else { + $oMolliePayment = $_SESSION['oMolliePayment']; + } + $logData .= '$' . $oMolliePayment->id; + $this->doLog('Mollie Create Payment Redirect: ' . $oMolliePayment->getCheckoutUrl() . "
" . print_r($oMolliePayment, 1) . "
", $logData, LOGLEVEL_DEBUG); + Payment::updateFromPayment($oMolliePayment, $order->kBestellung, md5(trim($hash, '_'))); + Shop::Smarty()->assign('oMolliePayment', $oMolliePayment); + if (!$this->duringCheckout) { + Session::getInstance()->cleanUp(); + } + header('Location: ' . $oMolliePayment->getCheckoutUrl()); + unset($_SESSION['oMolliePayment']); + echo "redirect to payment ..."; + exit(); + } catch (ApiException $e) { + $this->doLog("Create Payment Error: " . $e->getMessage() . '
' . print_r($orderData, 1) . '
', $logData, LOGLEVEL_ERROR); + header('Location: ' . Shop::getURL() . '/bestellvorgang.php?editZahlungsart=1&mollieStatus=failed'); + echo "redirect..."; + exit(); + } + } + + /** + * @return MollieApiClient + * @throws ApiException + * @throws IncompatiblePlatform + */ + public static function API() + { + Helper::init(); + if (self::$_mollie === null) { + self::$_mollie = new MollieApiClient(); + self::$_mollie->setApiKey(Helper::getSetting('api_key')); + self::$_mollie->addVersionString("JTL-Shop/" . JTL_VERSION . '.' . JTL_MINOR_VERSION); + self::$_mollie->addVersionString("ws_mollie/" . Helper::oPlugin()->nVersion); + } + return self::$_mollie; + } + + /** + * @param Bestellung $order + * @param $hash + * @return array + */ + protected function getOrderData(Bestellung $order, $hash) + { + $locale = self::getLocale($_SESSION['cISOSprache'], $_SESSION['Kunde']->cLand); + + $_currencyFactor = (float)$order->Waehrung->fFaktor; + + $data = [ + 'locale' => $locale ?: 'de_DE', + 'amount' => (object)[ + 'currency' => $order->Waehrung->cISO, + //'value' => number_format($order->fGesamtsummeKundenwaehrung, 2, '.', ''), + // runden auf 5 Rappen berücksichtigt + 'value' => number_format($this->optionaleRundung($order->fGesamtsumme * $_currencyFactor), 2, '.', ''), + ], + 'orderNumber' => utf8_encode($order->cBestellNr), + 'lines' => [], + 'billingAddress' => new stdClass(), + + 'redirectUrl' => (int)$this->duringCheckout ? Shop::getURL() . '/bestellabschluss.php?mollie=' . md5(trim($hash, '_')) : $this->getReturnURL($order), + 'webhookUrl' => $this->getNotificationURL($hash), // . '&hash=' . md5(trim($hash, '_')), + ]; + + if (static::MOLLIE_METHOD !== '') { + $data['method'] = static::MOLLIE_METHOD; + } + + if (static::MOLLIE_METHOD === \Mollie\Api\Types\PaymentMethod::CREDITCARD && array_key_exists('mollieCardToken', $_SESSION)) { + $data['payment'] = new stdClass(); + $data['payment']->cardToken = trim($_SESSION['mollieCardToken']); + } + + if ($organizationName = utf8_encode(trim($order->oRechnungsadresse->cFirma))) { + $data['billingAddress']->organizationName = $organizationName; + } + $data['billingAddress']->title = utf8_encode($order->oRechnungsadresse->cAnrede === 'm' ? Shop::Lang()->get('mr') : Shop::Lang()->get('mrs')); + $data['billingAddress']->givenName = utf8_encode($order->oRechnungsadresse->cVorname); + $data['billingAddress']->familyName = utf8_encode($order->oRechnungsadresse->cNachname); + $data['billingAddress']->email = utf8_encode($order->oRechnungsadresse->cMail); + $data['billingAddress']->streetAndNumber = utf8_encode($order->oRechnungsadresse->cStrasse . ' ' . $order->oRechnungsadresse->cHausnummer); + $data['billingAddress']->postalCode = utf8_encode($order->oRechnungsadresse->cPLZ); + $data['billingAddress']->city = utf8_encode($order->oRechnungsadresse->cOrt); + $data['billingAddress']->country = $order->oRechnungsadresse->cLand; + + if ($order->Lieferadresse != null) { + $data['shippingAddress'] = new stdClass(); + if ($organizationName = utf8_encode(trim($order->Lieferadresse->cFirma))) { + $data['shippingAddress']->organizationName = $organizationName; + } + $data['shippingAddress']->title = utf8_encode($order->Lieferadresse->cAnrede === 'm' ? Shop::Lang()->get('mr') : Shop::Lang()->get('mrs')); + $data['shippingAddress']->givenName = utf8_encode($order->Lieferadresse->cVorname); + $data['shippingAddress']->familyName = utf8_encode($order->Lieferadresse->cNachname); + $data['shippingAddress']->email = utf8_encode($order->oRechnungsadresse->cMail); + $data['shippingAddress']->streetAndNumber = utf8_encode($order->Lieferadresse->cStrasse . ' ' . $order->Lieferadresse->cHausnummer); + $data['shippingAddress']->postalCode = utf8_encode($order->Lieferadresse->cPLZ); + $data['shippingAddress']->city = utf8_encode($order->Lieferadresse->cOrt); + $data['shippingAddress']->country = $order->Lieferadresse->cLand; + } + + + /** @var WarenkorbPos $oPosition */ + foreach ($order->Positionen as $oPosition) { + + // EUR => 1 + $_netto = round($oPosition->fPreis, 2); + $_vatRate = (float)$oPosition->fMwSt / 100; + $_amount = (float)$oPosition->nAnzahl; + + $unitPriceNetto = round(($_currencyFactor * $_netto), 2); + $unitPrice = round($unitPriceNetto * (1 + $_vatRate), 2); + $totalAmount = round($_amount * $unitPrice, 2); + $vatAmount = round($totalAmount - ($totalAmount / (1 + $_vatRate)), 2); + + $line = new stdClass(); + $line->name = utf8_encode($oPosition->cName); + $line->quantity = $oPosition->nAnzahl; + $line->unitPrice = (object)[ + 'value' => number_format($unitPrice, 2, '.', ''), + 'currency' => $order->Waehrung->cISO, + ]; + $line->totalAmount = (object)[ + 'value' => number_format($totalAmount, 2, '.', ''), + 'currency' => $order->Waehrung->cISO, + ]; + $line->vatRate = "{$oPosition->fMwSt}"; + + $line->vatAmount = (object)[ + 'value' => number_format($vatAmount, 2, '.', ''), + 'currency' => $order->Waehrung->cISO, + ]; + + switch ((int)$oPosition->nPosTyp) { + case (int)C_WARENKORBPOS_TYP_GRATISGESCHENK: + case (int)C_WARENKORBPOS_TYP_ARTIKEL: + $line->type = OrderLineType::TYPE_PHYSICAL; + $line->sku = $oPosition->cArtNr; + break; + case (int)C_WARENKORBPOS_TYP_VERSANDPOS: + $line->type = OrderLineType::TYPE_SHIPPING_FEE; + break; + case (int)C_WARENKORBPOS_TYP_VERPACKUNG: + case (int)C_WARENKORBPOS_TYP_VERSANDZUSCHLAG: + case (int)C_WARENKORBPOS_TYP_ZAHLUNGSART: + case (int)C_WARENKORBPOS_TYP_VERSAND_ARTIKELABHAENGIG: + case (int)C_WARENKORBPOS_TYP_TRUSTEDSHOPS: + $line->type = OrderLineType::TYPE_SURCHARGE; + break; + case (int)C_WARENKORBPOS_TYP_GUTSCHEIN: + case (int)C_WARENKORBPOS_TYP_KUPON: + case (int)C_WARENKORBPOS_TYP_NEUKUNDENKUPON: + $line->type = OrderLineType::TYPE_DISCOUNT; + break; + } + if (isset($line->type)) { + $data['lines'][] = $line; + } + } + + if ((int)$order->GuthabenNutzen === 1 && $order->fGuthaben < 0) { + $line = new stdClass(); + $line->type = OrderLineType::TYPE_STORE_CREDIT; + $line->name = 'Guthaben'; + $line->quantity = 1; + $line->unitPrice = (object)[ + 'value' => number_format($order->Waehrung->fFaktor * $order->fGuthaben, 2, '.', ''), + 'currency' => $order->Waehrung->cISO, + ]; + $line->unitPrice = (object)[ + 'value' => number_format($order->Waehrung->fFaktor * $order->fGuthaben, 2, '.', ''), + 'currency' => $order->Waehrung->cISO, + ]; + $line->totalAmount = (object)[ + 'value' => number_format($order->Waehrung->fFaktor * $order->fGuthaben, 2, '.', ''), + 'currency' => $order->Waehrung->cISO, + ]; + $line->vatRate = "0.00"; + $line->vatAmount = (object)[ + 'value' => number_format(0, 2, '.', ''), + 'currency' => $order->Waehrung->cISO, + ]; + $data['lines'][] = $line; + } + + // RUNDUNGSAUSGLEICH + $sum = .0; + foreach ($data['lines'] as $line) { + $sum += (float)$line->totalAmount->value; + } + if (abs($sum - (float)$data['amount']->value) > 0) { + $diff = (round((float)$data['amount']->value - $sum, 2)); + if ($diff != 0) { + $line = new stdClass(); + $line->type = $diff > 0 ? OrderLineType::TYPE_SURCHARGE : OrderLineType::TYPE_DISCOUNT; + $line->name = 'Rundungsausgleich'; + $line->quantity = 1; + $line->unitPrice = (object)[ + 'value' => number_format($diff, 2, '.', ''), + 'currency' => $order->Waehrung->cISO, + ]; + $line->unitPrice = (object)[ + 'value' => number_format($diff, 2, '.', ''), + 'currency' => $order->Waehrung->cISO, + ]; + $line->totalAmount = (object)[ + 'value' => number_format($diff, 2, '.', ''), + 'currency' => $order->Waehrung->cISO, + ]; + $line->vatRate = "0.00"; + $line->vatAmount = (object)[ + 'value' => number_format(0, 2, '.', ''), + 'currency' => $order->Waehrung->cISO, + ]; + $data['lines'][] = $line; + } + } + return $data; + } + + public static function getLocale($cISOSprache, $country = null) + { + switch ($cISOSprache) { + case "ger": + if ($country === "AT") { + return "de_AT"; + } + if ($country === "CH") { + return "de_CH"; + } + return "de_DE"; + case "fre": + if ($country === "BE") { + return "fr_BE"; + } + return "fr_FR"; + case "dut": + if ($country === "BE") { + return "nl_BE"; + } + return "nl_NL"; + case "spa": + return "es_ES"; + case "ita": + return "it_IT"; + case "pol": + return "pl_PL"; + case "hun": + return "hu_HU"; + case "por": + return "pt_PT"; + case "nor": + return "nb_NO"; + case "swe": + return "sv_SE"; + case "fin": + return "fi_FI"; + case "dan": + return "da_DK"; + case "ice": + return "is_IS"; + default: + return "en_US"; + } + } + + public function optionaleRundung($gesamtsumme) + { + $conf = Shop::getSettings([CONF_KAUFABWICKLUNG]); + if (isset($conf['kaufabwicklung']['bestellabschluss_runden5']) && $conf['kaufabwicklung']['bestellabschluss_runden5'] == 1) { + $waehrung = isset($_SESSION['Waehrung']) ? $_SESSION['Waehrung'] : null; + if ($waehrung === null || !isset($waehrung->kWaehrung)) { + $waehrung = Shop::DB()->select('twaehrung', 'cStandard', 'Y'); + } + $faktor = $waehrung->fFaktor; + $gesamtsumme *= $faktor; + + // simplification. see https://de.wikipedia.org/wiki/Rundung#Rappenrundung + $gesamtsumme = round($gesamtsumme * 20) / 20; + $gesamtsumme /= $faktor; + } + + return $gesamtsumme; + } + + public function updateHash($hash, $orderID) + { + $hash = trim($hash, '_'); + $_upd = new stdClass(); + $_upd->cNotifyID = $orderID; + return Shop::DB()->update('tzahlungsession', 'cZahlungsID', $hash, $_upd); + } + + /** + * @param Bestellung $order + * @param string $hash + * @param array $args + */ + public function handleNotification($order, $hash, $args) + { + + $logData = '#' . $order->kBestellung . "§" . $order->cBestellNr; + $this->doLog('JTLMollie::handleNotification
' . print_r([$hash, $args], 1) . '
', $logData, LOGLEVEL_DEBUG); + + try { + + $oMolliePayment = self::API()->orders->get($args['id'], ['embed' => 'payments']); + Mollie::handleOrder($oMolliePayment, $order->kBestellung); + + } catch (Exception $e) { + $this->doLog('JTLMollie::handleNotification: ' . $e->getMessage(), $logData); + } + } + + /** + * @param Bestellung $order + * @param string $hash + * @param array $args + * + * @return true, if $order should be finalized + */ + public function finalizeOrder($order, $hash, $args) + { + + $result = false; + try { + if ($oZahlungSession = self::getZahlungSession(md5($hash))) { + if ((int)$oZahlungSession->kBestellung <= 0) { + + $logData = '$' . $args['id']; + $GLOBALS['mollie_notify_lock'] = new \ws_mollie\ExclusiveLock('mollie_' . $args['id'], PFAD_ROOT . PFAD_COMPILEDIR); + if ($GLOBALS['mollie_notify_lock']->lock()) { + $this->doLog("JTLMollie::finalizeOrder::locked ({$args['id']})", $logData, LOGLEVEL_DEBUG); + } else { + $this->doLog("JTLMollie::finalizeOrder::locked failed ({$args['id']})", $logData, LOGLEVEL_ERROR); + } + + $oOrder = self::API()->orders->get($args['id'], ['embed' => 'payments']); + + $result = in_array($oOrder->status, [OrderStatus::STATUS_PAID, OrderStatus::STATUS_AUTHORIZED, OrderStatus::STATUS_PENDING, OrderStatus::STATUS_COMPLETED]); + $this->doLog('JTLMollie::finalizeOrder (' . ($result ? 'true' : 'false') . ')
' . print_r([$hash, $args, $oOrder], 1) . '
', $logData, LOGLEVEL_DEBUG); + //Payment::updateFromPayment($oMolliePayment, $order->kBestellung); + } + } + } catch (Exception $e) { + $this->doLog('JTLMollie::finalizeOrder: ' . $e->getMessage(), "#" . $hash); + } + return $result; + } + + public static function getZahlungSession($hash) + { + return Shop::DB()->executeQueryPrepared("SELECT * FROM tzahlungsession WHERE MD5(cZahlungsID) = :cZahlungsID", [':cZahlungsID' => trim($hash, '_')], 1); + } + + /** + * @return bool + */ + public function canPayAgain() + { + return true; + } + + /** + * determines, if the payment method can be selected in the checkout process + * + * @return bool + */ + public function isSelectable() + { + /** @var Warenkorb $wk */ + $wk = $_SESSION['Warenkorb']; + foreach ($wk->PositionenArr as $oPosition) { + if ((int)$oPosition->nPosTyp === (int)C_WARENKORBPOS_TYP_ARTIKEL && $oPosition->Artikel && $oPosition->Artikel->cTeilbar === 'Y' + && fmod($oPosition->nAnzahl, 1) !== 0.0) { + return false; + } + } + + $locale = self::getLocale($_SESSION['cISOSprache'], $_SESSION['Kunde']->cLand); + if (static::MOLLIE_METHOD !== '') { + try { + $method = self::PossiblePaymentMethods(static::MOLLIE_METHOD, $locale, $_SESSION['Kunde']->cLand, $_SESSION['Waehrung']->cISO, $wk->gibGesamtsummeWaren(true) * $_SESSION['Waehrung']->fFaktor); + if ($method !== null) { + + if ((int)$this->duringCheckout === 1 && !static::ALLOW_PAYMENT_BEFORE_ORDER) { + $this->doLog(static::MOLLIE_METHOD . " cannot be used for payment before order."); + return false; + } + + $this->updatePaymentMethod($_SESSION['cISOSprache'], $method); + $this->cBild = $method->image->size2x; + return true; + } + return false; + } catch (Exception $e) { + $this->doLog('Method ' . static::MOLLIE_METHOD . ' not selectable:' . $e->getMessage()); + return false; + } + } else { + $this->doLog("Global mollie PaymentMethod cannot be used for payments directly."); + } + return false; + } + + /** + * @param $method + * @param $locale + * @param $billingCountry + * @param $currency + * @param $amount + * @return mixed|null + * @throws ApiException + * @throws IncompatiblePlatform + */ + protected static function PossiblePaymentMethods($method, $locale, $billingCountry, $currency, $amount) + { + $key = md5(serialize([$locale, $billingCountry, $amount, $currency])); + if (!array_key_exists($key, self::$_possiblePaymentMethods)) { + self::$_possiblePaymentMethods[$key] = self::API()->methods->allActive(['amount' => ['currency' => $currency, 'value' => number_format($amount, 2, '.', '')], 'billingCountry' => $_SESSION['Kunde']->cLand, 'locale' => $locale, 'includeWallets' => 'applepay', 'include' => 'pricing,issuers', 'resource' => 'orders']); + } + + if ($method !== null) { + foreach (self::$_possiblePaymentMethods[$key] as $m) { + if ($m->id === $method) { + return $m; + } + } + return null; + } + return self::$_possiblePaymentMethods[$key]; + } + + /** + * @param $cISOSprache + * @param $method + */ + protected function updatePaymentMethod($cISOSprache, $method) + { + if (Helper::getSetting('paymentmethod_sync') === 'N') { + return; + } + $size = Helper::getSetting('paymentmethod_sync'); + if ((!isset($this->cBild) || $this->cBild === '') && isset($method->image->$size)) { + Shop::DB()->executeQueryPrepared("UPDATE tzahlungsart SET cBild = :cBild WHERE cModulId = :cModulId", [':cBild' => $method->image->$size, ':cModulId' => $this->cModulId], 3); + } + if ($za = Shop::DB()->executeQueryPrepared('SELECT kZahlungsart FROM tzahlungsart WHERE cModulID = :cModulID', [':cModulID' => $this->moduleID], 1)) { + Shop::DB()->executeQueryPrepared("INSERT INTO tzahlungsartsprache (kZahlungsart, cISOSprache, cName, cGebuehrname, cHinweisText) VALUES (:kZahlungsart, :cISOSprache, :cName, :cGebuehrname, :cHinweisText) ON DUPLICATE KEY UPDATE cName = IF(cName = '',:cName1,cName), cHinweisTextShop = IF(cHinweisTextShop = '' || cHinweisTextShop IS NULL,:cHinweisTextShop,cHinweisTextShop);", [ + ':kZahlungsart' => (int)$za->kZahlungsart, + ':cISOSprache' => $cISOSprache, + ':cName' => utf8_decode($method->description), + ':cGebuehrname' => '', + ':cHinweisText' => '', + ':cHinweisTextShop' => utf8_decode($method->description), + 'cName1' => $method->description, + ], 3); + } + } + + /** + * @param array $args_arr + * @return bool + */ + public function isValidIntern($args_arr = []) + { + if (Helper::getSetting("api_key")) { + return true; + } + $this->doLog("isValdid failed: init failed or no API Key given. Try clear the Cache."); + return false; + } +} diff --git a/version/106/paymentmethod/JTLMollieApplePay.php b/version/106/paymentmethod/JTLMollieApplePay.php new file mode 100644 index 0000000..ecf20a8 --- /dev/null +++ b/version/106/paymentmethod/JTLMollieApplePay.php @@ -0,0 +1,8 @@ +assign('profileId',$profileId) + ->assign('locale', self::getLocale($_SESSION['cISOSprache'], $_SESSION['Kunde']->cLand)) + ->assign('testmode', strpos(Helper::getSetting('api_key'), 'test_') === 0) + ->assign('mollieLang', Helper::oPlugin()->oPluginSprachvariableAssoc_arr) + ->assign('trustBadge', Helper::getSetting('loadTrust') === 'Y' ? Helper::oPlugin()->cFrontendPfadURLSSL . 'img/trust_' . $_SESSION['cISOSprache'] . '.png' : false); + + return false; + } + + +} diff --git a/version/106/paymentmethod/JTLMollieDirectDebit.php b/version/106/paymentmethod/JTLMollieDirectDebit.php new file mode 100644 index 0000000..ce0441d --- /dev/null +++ b/version/106/paymentmethod/JTLMollieDirectDebit.php @@ -0,0 +1,8 @@ + + {$oMollieException->getMessage()} + +{/if} \ No newline at end of file diff --git a/version/106/paymentmethod/tpl/mollieComponents.tpl b/version/106/paymentmethod/tpl/mollieComponents.tpl new file mode 100644 index 0000000..9ad70bc --- /dev/null +++ b/version/106/paymentmethod/tpl/mollieComponents.tpl @@ -0,0 +1,101 @@ +

{$mollieLang.cctitle}

+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+ + +
+ +
+ {if $trustBadge} +
+ PCI-DSS SAQ-A compliant +
+ {/if} +
+ + + + + \ No newline at end of file