From 7008bddf13fbd060526aeb092bfd3f0e052a6fd9 Mon Sep 17 00:00:00 2001 From: "C. Proske" Date: Fri, 15 May 2020 16:50:11 +0200 Subject: [PATCH] version 108 --- composer.lock | 290 ++++++- info.xml | 14 +- version/108/adminmenu/info.php | 64 ++ version/108/adminmenu/orders.php | 240 ++++++ version/108/adminmenu/paymentmethods.php | 60 ++ version/108/adminmenu/tpl/info.tpl | 96 +++ .../tpl/mollie-account-erstellen.png | Bin 0 -> 38998 bytes version/108/adminmenu/tpl/order.tpl | 286 +++++++ version/108/adminmenu/tpl/orders.tpl | 135 +++ version/108/adminmenu/tpl/paymentmethods.tpl | 97 +++ version/108/class/ExclusiveLock.php | 76 ++ version/108/class/Helper.php | 311 +++++++ version/108/class/Model/AbstractModel.php | 7 + version/108/class/Model/Payment.php | 84 ++ version/108/class/Mollie.php | 550 +++++++++++++ version/108/frontend/131_globalinclude.php | 120 +++ version/108/frontend/140_smarty.php | 78 ++ version/108/frontend/144_notify.php | 63 ++ version/108/frontend/181_sync.php | 43 + version/108/frontend/img/trust_eng.png | Bin 0 -> 15299 bytes version/108/frontend/img/trust_fre.png | Bin 0 -> 17319 bytes version/108/frontend/img/trust_ger.png | Bin 0 -> 15352 bytes version/108/paymentmethod/JTLMollie.php | 779 ++++++++++++++++++ .../108/paymentmethod/JTLMollieApplePay.php | 8 + .../108/paymentmethod/JTLMollieBancontact.php | 8 + .../paymentmethod/JTLMollieBanktransfer.php | 10 + .../108/paymentmethod/JTLMollieBelfius.php | 8 + .../108/paymentmethod/JTLMollieCreditCard.php | 43 + .../paymentmethod/JTLMollieDirectDebit.php | 8 + version/108/paymentmethod/JTLMollieEPS.php | 8 + .../108/paymentmethod/JTLMollieGiftcard.php | 8 + .../108/paymentmethod/JTLMollieGiropay.php | 8 + version/108/paymentmethod/JTLMollieIDEAL.php | 8 + .../108/paymentmethod/JTLMollieINGHomePay.php | 8 + version/108/paymentmethod/JTLMollieKBC.php | 8 + .../paymentmethod/JTLMollieKlarnaPayLater.php | 8 + .../paymentmethod/JTLMollieKlarnaSliceIt.php | 8 + version/108/paymentmethod/JTLMollieMyBank.php | 8 + version/108/paymentmethod/JTLMolliePayPal.php | 8 + .../paymentmethod/JTLMolliePaysafecard.php | 8 + .../108/paymentmethod/JTLMolliePrzelewy24.php | 8 + version/108/paymentmethod/JTLMollieSofort.php | 8 + .../paymentmethod/tpl/bestellabschluss.tpl | 5 + .../paymentmethod/tpl/mollieComponents.tpl | 103 +++ version/108/sql/107.sql | 4 + 45 files changed, 3670 insertions(+), 24 deletions(-) create mode 100644 version/108/adminmenu/info.php create mode 100644 version/108/adminmenu/orders.php create mode 100644 version/108/adminmenu/paymentmethods.php create mode 100644 version/108/adminmenu/tpl/info.tpl create mode 100644 version/108/adminmenu/tpl/mollie-account-erstellen.png create mode 100644 version/108/adminmenu/tpl/order.tpl create mode 100644 version/108/adminmenu/tpl/orders.tpl create mode 100644 version/108/adminmenu/tpl/paymentmethods.tpl create mode 100644 version/108/class/ExclusiveLock.php create mode 100644 version/108/class/Helper.php create mode 100644 version/108/class/Model/AbstractModel.php create mode 100644 version/108/class/Model/Payment.php create mode 100644 version/108/class/Mollie.php create mode 100644 version/108/frontend/131_globalinclude.php create mode 100644 version/108/frontend/140_smarty.php create mode 100644 version/108/frontend/144_notify.php create mode 100644 version/108/frontend/181_sync.php create mode 100644 version/108/frontend/img/trust_eng.png create mode 100644 version/108/frontend/img/trust_fre.png create mode 100644 version/108/frontend/img/trust_ger.png create mode 100644 version/108/paymentmethod/JTLMollie.php create mode 100644 version/108/paymentmethod/JTLMollieApplePay.php create mode 100644 version/108/paymentmethod/JTLMollieBancontact.php create mode 100644 version/108/paymentmethod/JTLMollieBanktransfer.php create mode 100644 version/108/paymentmethod/JTLMollieBelfius.php create mode 100644 version/108/paymentmethod/JTLMollieCreditCard.php create mode 100644 version/108/paymentmethod/JTLMollieDirectDebit.php create mode 100644 version/108/paymentmethod/JTLMollieEPS.php create mode 100644 version/108/paymentmethod/JTLMollieGiftcard.php create mode 100644 version/108/paymentmethod/JTLMollieGiropay.php create mode 100644 version/108/paymentmethod/JTLMollieIDEAL.php create mode 100644 version/108/paymentmethod/JTLMollieINGHomePay.php create mode 100644 version/108/paymentmethod/JTLMollieKBC.php create mode 100644 version/108/paymentmethod/JTLMollieKlarnaPayLater.php create mode 100644 version/108/paymentmethod/JTLMollieKlarnaSliceIt.php create mode 100644 version/108/paymentmethod/JTLMollieMyBank.php create mode 100644 version/108/paymentmethod/JTLMolliePayPal.php create mode 100644 version/108/paymentmethod/JTLMolliePaysafecard.php create mode 100644 version/108/paymentmethod/JTLMolliePrzelewy24.php create mode 100644 version/108/paymentmethod/JTLMollieSofort.php create mode 100644 version/108/paymentmethod/tpl/bestellabschluss.tpl create mode 100644 version/108/paymentmethod/tpl/mollieComponents.tpl create mode 100644 version/108/sql/107.sql diff --git a/composer.lock b/composer.lock index 1e88740..937ee68 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "composer/ca-bundle", - "version": "1.2.6", + "version": "1.2.7", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "47fe531de31fca4a1b997f87308e7d7804348f7e" + "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd" }, "dist": { "type": "zip", - "url": "https://github.com/gitapi/repos/composer/ca-bundle/zipball/47fe531de31fca4a1b997f87308e7d7804348f7e", - "reference": "47fe531de31fca4a1b997f87308e7d7804348f7e", + "url": "https://github.com/gitapi/repos/composer/ca-bundle/zipball/95c63ab2117a72f48f5a55da9740a3273d45b7fd", + "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd", "shasum": "" }, "require": { @@ -60,27 +60,38 @@ "ssl", "tls" ], - "time": "2020-01-13T10:02:55+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2020-04-08T08:27:21+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "6.5.2", + "version": "6.5.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "43ece0e75098b7ecd8d13918293029e555a50f82" + "reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e" }, "dist": { "type": "zip", - "url": "https://github.com/gitapi/repos/guzzle/guzzle/zipball/43ece0e75098b7ecd8d13918293029e555a50f82", - "reference": "43ece0e75098b7ecd8d13918293029e555a50f82", + "url": "https://github.com/gitapi/repos/guzzle/guzzle/zipball/aab4ebd862aa7d04f01a4b51849d657db56d882e", + "reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^1.0", "guzzlehttp/psr7": "^1.6.1", - "php": ">=5.5" + "php": ">=5.5", + "symfony/polyfill-intl-idn": "^1.11" }, "require-dev": { "ext-curl": "*", @@ -88,7 +99,6 @@ "psr/log": "^1.1" }, "suggest": { - "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", @@ -127,7 +137,7 @@ "rest", "web service" ], - "time": "2019-12-23T11:57:10+00:00" + "time": "2020-04-18T10:38:46+00:00" }, { "name": "guzzlehttp/promises", @@ -430,6 +440,17 @@ { "name": "roave/security-advisories", "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Roave/SecurityAdvisories.git", + "reference": "2bdae3cc8428d637d5c86c8c33d0a3354ce93f7f" + }, + "dist": { + "type": "zip", + "url": "https://github.com/gitapi/repos/Roave/SecurityAdvisories/zipball/2bdae3cc8428d637d5c86c8c33d0a3354ce93f7f", + "reference": "2bdae3cc8428d637d5c86c8c33d0a3354ce93f7f", + "shasum": "" + }, "conflict": { "3f/pygmentize": "<1.2", "adodb/adodb-php": "<5.20.12", @@ -468,11 +489,13 @@ "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", "dolibarr/dolibarr": "<=10.0.6", "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=7,<7.69|>=8,<8.7.11|>=8.8,<8.8.1", - "drupal/drupal": ">=7,<7.69|>=8,<8.7.11|>=8.8,<8.8.1", + "drupal/core": ">=7,<7.69|>=8,<8.7.12|>=8.8,<8.8.4", + "drupal/drupal": ">=7,<7.69|>=8,<8.7.12|>=8.8,<8.8.4", "endroid/qr-code-bundle": "<3.4.2", "enshrined/svg-sanitize": "<0.13.1", "erusev/parsedown": "<1.7.2", + "ezsystems/demobundle": ">=5.4,<5.4.6.1", + "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1", "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1", "ezsystems/ezplatform": ">=1.7,<1.7.9.1|>=1.13,<1.13.5.1|>=2.5,<2.5.4", "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6", @@ -485,6 +508,7 @@ "firebase/php-jwt": "<2", "fooman/tcpdf": "<6.2.22", "fossar/tcpdf-parser": "<6.2.22", + "friendsofsymfony/oauth2-php": "<1.3", "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", "fuel/core": "<1.8.1", @@ -496,6 +520,7 @@ "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30", "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "illuminate/view": ">=7,<7.1.2", "ivankristianto/phpwhois": "<=4.3", "james-heinrich/getid3": "<1.9.9", "joomla/session": "<1.3.1", @@ -503,7 +528,7 @@ "kazist/phpwhois": "<=4.2.6", "kreait/firebase-php": ">=3.2,<3.8.1", "la-haute-societe/tcpdf": "<6.2.22", - "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30", + "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30|>=7,<7.1.2", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", "league/commonmark": "<0.18.3", "librenms/librenms": "<1.53", @@ -546,7 +571,7 @@ "serluck/phpwhois": "<=4.2.6", "shopware/shopware": "<5.3.7", "silverstripe/admin": ">=1.0.3,<1.0.4|>=1.1,<1.1.1", - "silverstripe/assets": ">=1,<1.3.5|>=1.4,<1.4.4", + "silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2", "silverstripe/cms": "<4.3.6|>=4.4,<4.4.4", "silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", @@ -567,6 +592,7 @@ "socalnick/scn-social-auth": "<1.15.2", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", + "ssddanbrown/bookstack": "<0.25.3", "stormpath/sdk": ">=0,<9.9.99", "studio-42/elfinder": "<2.1.49", "swiftmailer/swiftmailer": ">=4,<5.4.5", @@ -579,9 +605,10 @@ "symbiote/silverstripe-versionedfiles": "<=2.0.3", "symfony/cache": ">=3.1,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", - "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", + "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", "symfony/http-kernel": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", "symfony/mime": ">=4.3,<4.3.8", @@ -590,14 +617,14 @@ "symfony/polyfill-php55": ">=1,<1.10", "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/routing": ">=2,<2.0.19", - "symfony/security": ">=2,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/security": ">=2,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=4.4,<4.4.7|>=5,<5.0.7", "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.37|>=3,<3.3.17|>=3.4,<3.4.7|>=4,<4.0.7", "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", "symfony/serializer": ">=2,<2.0.11", - "symfony/symfony": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", + "symfony/symfony": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", "symfony/translation": ">=2,<2.0.17", "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", @@ -674,7 +701,225 @@ } ], "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2020-03-03T17:52:54+00:00" + "time": "2020-04-21T14:24:08+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf" + }, + "dist": { + "type": "zip", + "url": "https://github.com/gitapi/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", + "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-09T19:04:49+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" + }, + "dist": { + "type": "zip", + "url": "https://github.com/gitapi/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-09T19:04:49+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "37b0976c78b94856543260ce09b460a7bc852747" + }, + "dist": { + "type": "zip", + "url": "https://github.com/gitapi/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747", + "reference": "37b0976c78b94856543260ce09b460a7bc852747", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-02-27T09:26:54+00:00" } ], "packages-dev": [], @@ -686,5 +931,6 @@ "prefer-stable": false, "prefer-lowest": false, "platform": [], - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "1.1.0" } diff --git a/info.xml b/info.xml index 6ba23dc..c866c6c 100644 --- a/info.xml +++ b/info.xml @@ -33,6 +33,9 @@ 107.sql 2020-03-02 + + 2020-04-24 + 131_globalinclude.php 144_notify.php @@ -207,6 +210,13 @@ + + mcErrorMessage + Ein oder mehrere Felder sind ungültig. + + + + @@ -242,8 +252,8 @@ 0 JTLMollieCreditCard.php JTLMollieCreditCard - tpl/mollieComponents.tpl tpl/bestellabschluss.tpl + tpl/mollieComponents.tpl Kreditkarte mollie @@ -425,7 +435,7 @@ JTLMollieIDEAL tpl/bestellabschluss.tpl - Giropay + iDEAL mollie Bezahlen Sie bequem mit iDEAL. diff --git a/version/108/adminmenu/info.php b/version/108/adminmenu/info.php new file mode 100644 index 0000000..292064e --- /dev/null +++ b/version/108/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/108/adminmenu/orders.php b/version/108/adminmenu/orders.php new file mode 100644 index 0000000..1704bf2 --- /dev/null +++ b/version/108/adminmenu/orders.php @@ -0,0 +1,240 @@ +executeQueryPrepared('SELECT * FROM xplugin_ws_mollie_payments WHERE kBestellung > 0 AND dCreatedAt >= :From AND dCreatedAt <= :To ORDER BY dCreatedAt', [ + ':From' => $from->format('Y-m-d'), + ':To' => $to->format('Y-m-d'), + ], 2); + + + $api = JTLMollie::API(); + + header('Content-Type: application/csv'); + header('Content-Disposition: attachment; filename=mollie-' . $from->format('Ymd') . '-' . $to->format('Ymd') . '.csv'); + header('Pragma: no-cache'); + + $out = fopen('php://output', 'w'); + + + fputcsv($out, [ + 'kBestellung', + 'OrderID', + 'Status (mollie)', + 'BestellNr', + 'Status (JTL)', + 'Mode', + 'OriginalOrderNumber', + 'Currency', + 'Amount', + 'Method', + 'PaymentID', + 'Created' + ]); + + + foreach ($orders as $order) { + $tbestellung = Shop::DB()->executeQueryPrepared('SELECT cBestellNr, cStatus FROM tbestellung WHERE kBestellung = :kBestellung', [':kBestellung' => $order->kBestellung], 1); + + $tmp = [ + 'kBestellung' => $order->kBestellung, + 'cOrderId' => $order->kID, + 'cStatus' => $order->cStatus, + 'cBestellNr' => $tbestellung ? $tbestellung->cBestellNr : $order->cOrderNumber, + 'nStatus' => $tbestellung ? $tbestellung->cStatus : 0, + 'cMode' => $order->cMode, + 'cOriginalOrderNumber' => '', + 'cCurrency' => $order->cCurrency, + 'fAmount' => $order->fAmount, + 'cMethod' => $order->cMethod, + 'cPaymentId' => '', + 'dCreated' => $order->dCreatedAt, + ]; + + try { + $oOrder = $api->orders->get($order->kID, ['embed' => 'payments']); + $tmp['cStatus'] = $oOrder->status; + $tmp['cOriginalOrderNumber'] = isset($oOrder->metadata->originalOrderNumber) ? $oOrder->metadata->originalOrderNumber : ''; + foreach ($oOrder->payments() as $payment) { + if ($payment->status === \Mollie\Api\Types\PaymentStatus::STATUS_PAID) { + $tmp['cPaymentId'] = $payment->id; + } + } + } catch (Exception $e) { + } + fputcsv($out, $tmp); + + $export[] = $tmp; + } + + fclose($out); + exit(); + + } catch (Exception $e) { + $ordersMsgs[] = (object)['type' => 'danger', 'text' => 'Fehler:' . $e->getMessage()]; + } + break; + + case 'refund': + 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; + } + $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 = utf8_encode($oBestellung->cVersandartName); + $tracking->url = utf8_encode($oBestellung->cTrackingURL); + $tracking->code = utf8_encode($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; + } + } + + + Mollie::fixZahlungsarten(); + + + $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/108/adminmenu/paymentmethods.php b/version/108/adminmenu/paymentmethods.php new file mode 100644 index 0000000..1782978 --- /dev/null +++ b/version/108/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/108/adminmenu/tpl/info.tpl b/version/108/adminmenu/tpl/info.tpl new file mode 100644 index 0000000..77a48dc --- /dev/null +++ b/version/108/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/108/adminmenu/tpl/mollie-account-erstellen.png b/version/108/adminmenu/tpl/mollie-account-erstellen.png new file mode 100644 index 0000000000000000000000000000000000000000..fc1819255ef725fa214cf2bf028cf3428794ecee GIT binary patch literal 38998 zcmaHScOYEP*Y_^M3StQ%QFa#*T@cZCSv^`p^cKDM&gvUIY={;uM2KiX^p+?=2+=#y zd$(9+y}r-$`#tab$NPKckDa-5&pC7EoO92;GxOQ#=jw_Sw;$XF000!qN^+V203j3r zAkYI5-t?4yc1PY+_dVtHJhfb`J$=mFtpGBXF6LHHWhXNmD@`jiOFy?BE6E!!cDt8) zo_cDk;ubDWd}ja9@cBBq-f#l|k_cZ{GYbbRPpG+-jh(X;%U)wE3)Ie1ibYpg?XjAx ztd*^ulE1r^mcRN-3x5X-F-sN%94hH6ej~ui%F_($>*VO{A?_>1@?UbrZ`%KK^Rqzz zi^S7GisiqR(o=g5m348qf(r9J<+TuaA`BG~;}du)^h8XI2P*hjK$QQnD8GOZufS9B zCy&Ji1)={cEH|UMTUv{2$|?M7teYz-7F$nGS8;xRA0Hn+A0a*$cN=~IF)=az$AbKV zg1k2rydHkeo@Tzh&K|7)mLO;4Vc~A)>S^cV4E;x+JC_ zGu;@B-`C8QUx4rNKU4ZQp_*kC%R~R{WY)9xh()7B`Nw zX8mUF|Q_m?g%j{mmeb6Xcr7Y|z(SE#J)KUNcia_HJQTe|poaQ+vMnwq$> zvxlddvxSwioD|EA6h1pUOK}+mc{u_3r$|Bh$4CJIc`>A*jEF2!Oh!)hvApaPA(4Oc z%DGs0IaxV-{+rkG|MJTIN8W$J!O8VzWH~E$J8vsX1$P%G=zmRH-0nZ?BK9BU{fpP~ zKkFj%A9?w2l;Qu!x&M!||J`*HLH{)WCv-)tB-y z0|eF|rmj9_o0*wWGP8{Vk&}~?`0mdIg}7_q><9NwuiqM8JJtZ>@s}k%0N?9t4dAsi z=V=BbVM;_~bo9pN=4Jn%@%7{4KbNZn3_w-KzOkb(!|evi@>k2lmdV8cqq63f7EI5% zvHqFl*Ve70i<6TRZH<_#jkV;y8X^Jd<(<>Kyu7Q))kkmez+C)owU39V^H-J+dRC*# zW;M=Qskq$hGCn_aZ8;&5(F-l^teoh++&(zDxO!C7dvtPX>LZTF9%wTg(AjdMOGT@krWO5A0v>0eNZ`S~Ef7ft>unGO;gTO}3P z%=_{cK;nII=ijSSoTP(X&#zxQ`+IXL@7^`e(QADYvGA;@`sQ0?GtFhkVOs3%7kGMc zm|5GO1@~Hb=9`wDzUuN`Lgcilq!g@{_d}Oq`Bg5xf$z^Sx{I4~eN!&3|GRZlUtgd@ z&Ph$ao}ZuJB&M5tF9}P90e~cNWjUFbzEj&-Z-R%%a3|59ewob;6&#TjPZK?=-ZXne z7Ge58Q*->50P_7?M+e*QCNYY}IwEGDDhVhnu;qwTvBVPA4Myb}+S}VLa9(9cef}}r zW=r{z|Bn$84w@MLD*}QrBn-+y^WW8o%bh6aEy;w-c*Z5{Oupb4%}^fxF<@ z$EqytqOHZD78ByF{Hh{M&BV6{jzEEiYiHWcDHiKLRsQ$|>8n-@4UmBe6hA(n;l5YA zzg7Rv)c^hp%L`l+eXi%XWX+;apO?pv8rBGtf1O`tuK#h-(b3@&Az9N7BBiI~#-W08 z^K!-oj#K&Hgia9`o%ee9o4>6b;n?2ZrlIE60ZNk(f8KTK--d*o*u=+#d-z_q)zjIS z^72C<5PtsF_q+uB(ZRZ|Q>6yFLs9vTiei*GhgeF zWaJ`Q*XMk#^gcv(9;=<6+-QCU2&E4IfpO7q_zeqsa zRsNz?K{eJJhox7)7j@du>qH4FOY>slCfU5~Omd!Dx|Q!GJ@b`?AKDFZGNZ{=?y*Ft z&PH;36veWBTGe4@H}v<4QMASrDq?XoGHe@)(eE2iJGU3=%F}ddI(WZFo&ij08 zXe5eS?;*%zFzie>1Vpf&VgG*3wA~nequ_?~G2z{Nh$63=9gHwh<`vdQ9s^$^eT?E? zE47i%{JxaW`EbmBu8qaMetAWhusWQa&*?aSF;N^)Z%N2UQ=Bz>+B`LDr)pf%6hePK z_hZtke9YO2UEd$LIk_~C^ODS17wGfWU_SrkMg0c$4m4^hXR35qw&icNYcA~}k|e3n znlpd`KWBe|^psj#Bg|8R4etH2yaRga!!jRagc#4kr%yJK$_}=_eRPqA!$9V<9kWKnkt(qm`w5SBvI7v{U|o2C=4ak z62^I34Gwt{!Dcz?<$U=SlwPSWA?iY&N;W^-eLq1cv3WN}cmx-8lguPE{EtMtH#+m`>2ci;n$+ zgz`O&tr2;wt_m>+VgzC~rCssFF3=0a*j-njAXn556Wnf@@m@*^3 zm-Gzdrx`1p^UO+cV~6wH>b~0c=fT+^nEyM34Qcv5uI%ESQ{cXo~%nU)&@xU{ySt`&8aN5*Pi~!!ZO7R%Gw&AHhOCA*7yC9VClL83#UNqRmV5uTODa3lu}P2 zN#a;nIHSU@7My+83wic@XJcLMv%L1cDvMyW)_7MG zB*|Jo!pz8a+vCD?hfdXF%M}5Zi_#ML*@RIF4ni?PEt@RpsZiuVO!=Oc67=``61AJ) zy)ayHm(_^CxZ&t?z_WV;Fm;}nf=714szADB(=B0EEg$jLBw%FE{=o(Wapi$T%NY^0uYR-))35S`ex5J%mU zPsjE9z`nE|pLuxDg$O!7pi>sbas;OOvk`lAd`_tIm3bM{Al$}K z|GflE#jnvbRGf;^_nhIcX4%;f$d2+;8`B3ccy`=Q!Ce_!l6MuE{`3%Y>ba@T{xNRXa zDs$=YpA419eATno1{hRwf^ZL4!odVlGzFVi@Sq@J@03p@IOv;IIW=m5&7xI1tK3P) zY=hu{pONbokm$V2&4gU^%p1gWqZm$sLMdEp(gnbk?FAR;ji!40I@(b&{Y z_~=|G%KmMCV(J^Ngeu%!3Ga;}MgySj7q4XT2i2%+JkgSD7{2M1d;&k>mcE&#MIk~A zmUJjU49h(682XW)uCB}D0nb4&Wd(Fjxx-~;mA6}(&wD{|4%jdb!*LBnIr8q5?e3AB4#_9K-}M?;t}9+*pFdx>}ETN=k-}50;Pr)f<_R zNb-Obrt{@i9%%}X@)Z4&?D{de47`QS(=p_F0ti9k&o2t`PkIkw=kV9OUyH^eHX($zYHxAhnjN9U&NRcCxJksS?!t|26qSe|#SB)%&%OiR9Y%~7 zcVS`T3rm7@nCdDuO`7vjWPAPZ&MU(tQEb|kN=mFXcGU6U^+0q7E794yG3lZVX)ea- zZNmdJShGw1?Ckx>;jbZO&f*u4p?F!)K#s16l1X)iRt!IEv^mBP&Xm6was$j8gr!bCx;_5{WBw#^RHdcd?84s z)~{OozF6O8br8g6#MOhPJLt!k@8T0%jWU*jsKPP^N{f@$&DJz2lNKV%c{)dMv}_bD zq6|c|uyn9QGEa?E%Enf!a#u#U*N!CH8;k9pfwK`VJb|PO7DaucCrrq01v8dHcsc?y zP;_yTim7;UbpPu**-7t_-%#&Wxc{7}+Q0yR8riokmJL)T2DZ03i0)Xj;3~vFehS&! z6Bh~3+DCU_=uvvVml>tUc=*m0YsX@Do#v*Fgi|x+#j*5wl-~(0?%<2PPO38WK#U5h zlz+V*jRQzCBLb(cYQ~z8fuM)zSA)FDvLGITEb*0g-R)X$_-nL%k5;t`2zQaHVt7{+ z8x$-KU$oQ@J%DfuCb1|NI@SU=#=OO~ax~tKd1~f*H5lji2hP2J>HwFDVv$QYhP*(_ zof^gMLmFQP`Sqla5IJisXy&?{@R!Cfjd;nr8yx^JYK+Y+mgesVFu| zC7K?gaI4YyU zE#Ft)y%VfG2jzJ}FoI!oxJ+j}!)e>cX9@8oVloy!>7HIB^o6Z-9&r{UYqNVYej?#n zE)VRAf6`HxdCrEvluNPdSY_=fqBMy(%8jhekp)avy6QELd@x~(tJj~!_7T&lgNj-! zxeG)?`u;pv-kj?@pTwJ`t2H#61Ug-Dd_zi)6CSnSY9>jee1J4b>tsQZD3_`8^TIwW8(cL3}b`<-->JzIT>DyR~mC{m;ilP3zqUtUvY2pSWL9m6V z`SZ)2375ZBT-VLi*V{% zl~q+zr{r&vc(FuM+b0OK#14^3j2qnIUM$)*o*jJiNAY6ee98(4O#yB|b_N{FOi#3J zcKn*pWdxQVH^_v`reQhajc|gcIzcat;3g+B)kdJ3wS|Zw5lZBk7K0ElK!@vZR<)wV_8Mk}mKzuVeLI_vea zmWQ8UwZsogz9(rR6r=*{WG#1^Hk_u4iDH7pb2O-!5AC=&><#XqOM)u`-&fWa=@GL# zBE_zr*#5cs5JvB#lw=s4v44mhynHNtM%gV%1-T1P?0vd;F23H*=>EauVsZ~q>Ae-T zpPO2oPwg9A^&BofG4Io^JRLg^X05ERtDi_N!#vwQeHJ4shh#(TJ_q!`iwQA(&wy+~|1+@I}Aq0~{bSnz-tt-#&p$=B{(_+&KvqtOG<0MnJ&B2;6cK2)SFW@j4Pj1_dbO{3NFqh~hQ%N-S$>ZGOyyUS}6- z8H3315F#CXPsjN8=!z9>9BLIp-r4k{q-ttZ7n0>9JgX2Yx`+@-vY9#n2At!3NBs#5 zIxl&?`twnaT3)EF!4O11+sgz1#S2T!|vKbp9sF+!Ejv=pIP<-i$k&{Ki-D^MLrH@W{Wl{s{tLWS8yGb3jcQ#;OIzOfx+SHCEJRKNIu(tl5KF_jz zOvvgn-XWIqB9L|p|3VKQ)cF#_8*V|jY>CWpe(-CFic*Ar@XT$A3bvfVa42$(KgwyW zrL!|m@gB0T_BVKi{7}Y4jFuppCG7UDs&_&YQm0rP@QMS8FnXcEhqO`m_9?&07)9Mx z(e&Uh7-#}{lmrRUZks2T$#y?pXDEs+csr2q!pGw{{b)w@?uv4Da;}H-{Z?TuDWg`DL(aroudhpiIl$bH3U^+Ip!LB+tye<_ZWWh{^7q4(CLQ~jTe(`O zwY^Eha*Jx2&nCZ;0!7ym-o|4&uy{QwjeHY+m3w0cR4ip(L-qIX0hjll6m)jRyk!6VvayB|sCve<1O)%MdaU>c3YWeU!!|iaIc{@e&9|pZc5!jbd~H18 zLEz!fLEbx1#h+&(jD&Z+KPAg|^2lMnu2RFG58+U@#`DWTiU-E<`u(1diaD1IEK0S* z;XK?KE5A8JoH;qQ_RhN#}^P zrS}WwktT;j!B3!dj5-&Jwbs8PX)xII-|fxmLfl7Ohd;H|sAb}97D{#b`&zVq%ZnfJ zX%Iz0!@z-9v1r(L;z8Z-kvfD=m?i&wlLJgP$i45&u+xBF1#c90ggNfp*RlhMF8#g` z09j4VSh_Q_ z_`6ZidFe2(k15UWEBU?s+uwaLGif1zA}|_=ny&yt0yz58{|!-!%9Q|+D$P?fRQ^L+ z)fi_ct6ZPV3_x2Q7s$}{cq3mY=D6;cthklq@KcY<{U1oUP>#KU=ww#4stcs60uUVs z8?R#z6r?=FUq}-b{h6J}&0-3{xNDD;)fv(cgqQ5l!J6NTO}pG|t=Rw+a`iqlmzvJa zb0?{5Qe1yVHk$rAtJcdW)Hz7QbMPdcmsEa^b{)~Q1+|t=GfKCdx02-qV=3fUj>z%_ zw-}1EivxzXQrPhM&BhCLoIw(~qg$JMu@LGl8O4$z%dPfp{5)oYbB?Wu=Kc~9n-Lk>L{ONH&-YdUdR9pSRo~%O@iM!=sk?8xj2Fd>8S6^hB=hbeP z#a(x&bAyu6QSI81BrpNOPO9f2h@D|kCUR-iT!v@e#fg^Ec?bKuZNbFP<=U}T4=$&GvF6ERc3O ztQsT>s()${JoidPe8Y5T{)iw-oEqPmS?tb_jnTnI?XgBDqfk5bNoSbS+dQXm z@5T?hB4)9h(i|5w}imG|J=-Aq#in()lYd4yi8D!V1eUJg?j zziF$aY<47t2?aj|bmh-nQ&;GAOdH^4qj!sW;KYt2dUMDY4al2@Ewid5Faz(I1|E@O z*RD@-kDd%utFC+putZX%Q%8V>u#jg;r_(MD)h<1@Jb!|NOsI%4ZUILIS6?;nL2GWM z_m=m)rT~#{v@wyxzQqP~0&U<{R6LHI%m>+i8&kbEP>f}=sXR8iJR+W?$E!SU|7;!j7ifC^kwl~g zcY7Vmw8$K)98DnCzl!`B{^yp5FW>Uu7>T->g z?)EYPkTOvY?I&!9 z#jkk&zI{th6WKL0L{-a(R1Xf?Yko8zv^6w%b%NMC0FtVGEqTTs6zMZXo(K_pd-)qR z;ae2BzK>`%K6-Ux2fMxt!H8N6;baSJAG*wy{kiS7=DB1&lIE$N znEbSld?GGRs5gLk#&7tE=6&(|m@LyQgL~*F+zP5wAGUVfNEg24Ma~~eCsu!xXXy7y zRgj|RlSgBssA?_6RzG@OryW;aOH1C^O$15`-<6*PIr?U^wlI_6lw0pbnB{~n!un2c3=mcBq*%t(O$F9!K#i#A&ud) z+|rY^zqjH^qJ@g1L%@j^gAFD1_0N6ODWaIgmXty;xNo?}LOG{#l8@5I+6d&{`h_Kk zJxGJ+unQe47DBpy zE?X4JiMBPE8-mG8afbqn;(C_n-CuLigLMPWI_$7^7?=hceJ zg$fFn=r?KWO9~T;GdMagF)+(-V(5=7)Vp6M+4+kFy;Gv}h=#(m=&!dx{q}TixrJQkkDiv*?cRclkOZX_K7Ytxus+BxF|XSJhWP|Os+D| zi7wD9PMt8n_3|B=(3XB>abXpIPWd4` zy|9xjfM6Q0EU$fr9UvQR`xYa~O4RUN4PzH9bbG31Dhu2v{7vZ1#E|$f;`^^yw6vkuNmbIe(p< zuf{DxLWTabUx8e8h>}=JC_9A^sJ2SN&Nt|~`-pSVBR>j7dL|5ue?QhL3gNUfV4AOm zHSzrUEUP2E&u9?<%lc)xs%&V^)$E#d|Fd%w&0XqutzkY-9;M(HI}>!vjT?im+Fp~5 zmzy?vpDJFx=KHe!@Jd#34CN)l2l<-gzCfMc=(tI^;_+qq>WPTpEduPzcC%e=|aMs1Ef7`9AYLqTTC1Gp8%R^FVw91K*{O z(;2rSkWsJ!Qf5H+5eX%8Aa+Zal3A*mst&S7eiJlx5x zq@VCMZCx7NnP)%tb18LJg9dE*=HsB~mwWxf86Fij9Rx&cK`d+LTRr^SN?!3=kG6aW z?uyWDe;V(8%+>HhO4gdWn%w~^Y~;8rdsx=MT8WC9OIVW4(`od&68qzdeGu9{h;`64 zMiQuJjqKj@o=->-30i;sX%;cze@z)YVf>|1o(@E+ZnC3R71Q*lNP%#?;q_iH;u6c<@In-7 za-ExDKY-9Qwy&_$!GJARY>LV8$HR9G*n)o{$YUWRHkWhU9MjHoc_gH;S)caV`h@Y-H6g++g_0O-596bw5<$C^;~o$U73bWga>W-L z`8p~i7?&k}|Kh;hMuZq*=s4K?q^*5y_fAHQV<;u5Dx*(LuqYR?cE5NnI-#;BWj4H8 z!Y8{vtTpJ>80A74*!_|#F+s3hG2jTN7$g<+%hJy}!%}}BMIW(tC#3XiPPZ#;MF^u z1S-!>&Y=SJP4_LkM5j-+;o*wseFDedawwx%I8%{c!LL}~IV$MxMp_b(ojWfLXA$)sWWCuEgSj4)W9uE4Rhm%m|Nz4cC!7Lw-J*d40 zp-dkxafY{20hWJy4ETN4fCL}EJ<-?*Jsxj``{E@ZfD;fRJv49HKYYu6psw^#_?b<% zQQy%a_sL}cLOY8bWl!a5!D_pP(w|5aMqKJslI7ctg;lzHm$BajX3;$lBJ(55HJFiq zvlG-nhQXg&FAt_}WtSNXn}WD^NH+O{s294;jURKYm1-bkM-Fo(Zl2HgXpAG9B0 zJBqggwv2-NQ+fp>DXHbME4Cuzs-HE?=P;md4yl}yyf$lHE{}4q2SYF6AGKJg#sAs<=lAloniu z6rL*2W~aaF)8CP-zwjhD`|yg}n&Jz%kZ-viyT$_FrqA=p5*OGUojtX+N$yfFE?7Vp z!>Jcj4gj!xg;WB9vdjW~^X|Pga?mLGOBlwP)WRU;DIczEGY`yfK3uuAq4V+vfZB!KaJL* z9GxyZ7g+(_kfyE3bvh+JT1Vd2YD85(b}hT%6J)nU>Mqq`;5#kKgSCUReQM;P6jUyt zAJ{=B6G7R8&)h;Nz|qFM2GUUHx(=IL7&!;3H2S&3TylgHljx!hvB?IqpVxpT|9zN-2iE# zGQ|%Vl7|OBx2tx3TeSxS&n28d2nwBlvMiGo4Bq6hmMIg~d$6EjJRJ#T5Ygo4J1>)g z9V~1y3D}Pj8`w360pyC?=D}uV6IOSJ11{>N@7T%%hw?zo@R=~SY35jP!ZpC-=4<5u z7A{c5lW^@zzlty)wR&5Fak3%4E2wD?(=NB+I;Tew-JEhx%}wBEwnj*nz>BAASLA#tX`v~ydQU|NJ{ zAP;=;RQ&#YPS>tiVh;v%_yZ+AFlH(hH=y`|kK2++y9xi01$pkaSs8Ur54!XNW3&Sf zSpO2r1!Nh$vM)GmzKL|odu_bAh}NU4MH4@VVxt+O;GfisZkq&1$t#s}ivy~4FG1)> zS3<%9OPreuqzeB_r5nl>v7UrGatp}vJ)@87iX7cyLpAUSh6)Qk10UwQoqd>{r!zmp zL|@|4&}W_x5AR{EI-Fv}Vf&F@_& zYZhZxYHv;5rex35XLW-KgV9-_9>F$UO zn4slaePqkhCB4dap$;`u*Ier((N`pNw^a*X=4p6?E|?j|pO3fNpN6B)G8;&!1P8nV z%&)0h`M9mGMl}%Ems+Y2>a?>!c=PX952kYxRz~(#*pJtch0NQ(8hb}fUj#GX>F&-k zL|ukbd*?QMv#9$t&cVn8pmBK0=#c8Nv*#oqckQZKMCkq4+{^l%mETrncbt0*&Nso7 zmh}=xVIB5DrS??yE~p;1?v*!hi27a=WaNj@&g%-E@V&&2n<6gv+f9S7irFK@y+HIK zM4W*A#%I3i?Te)Y4s^?di@H!$5l|GD?7Z2eK6R%+*yuTQN_-xPF+!*8&mjW@YvAP3PBcu}82 z`7EltC}SsSH=M-V|C7{i3bDYI*x!TPkEYlDrrRSua$!zkQ4jf<7x1WlO~%4Ib_Ov_ zQra6`8}oR}{w%wPQ$?poMSeEEyi8`hGM<;0TsP?>H9XUOApPS)Y{04e7eDc|6iU+o z5Gl*YYNU>?2%j*$p~_}?9qQ7m=xf!&>Dp5WpnLcA=H|Th?QBx-}>!!dZfi!#v_*fz$D;NL8Kww}QQ?iyuryyQRHh4liQAg9eKy&D(`$Zx;sl98G6L z1_Wr(H=9 zTgOQSa#m($1LSv3g!@KDnLEPqq3i-hrLULBpx50|JY`)%O?K@ZVsC+F~uLK-z z5(~F|*Sivt`~h8)gD~a61bh~`m22M z2vQkzsbE;tiCT8yvh;?j_0}+#8O6D8KB3ueGhlo~EY0Od*9$yQrm2Fx))e=B~ewee>+&t(W&L=P3=Ac!0OU*Gx{oH1C9zI5a z;uzP%;>OhkX1TJi-b!@mvu+XqQm8nn`pca#R9y%d={Y~eFeh9q`!l#i$}squW<{mG zB=r7-kIzYQpm@RT?+V94W-oG+>sk4q7fJmMBF+deTwSw|@3aV>;P%Ty z8+bcLA1>FK8h|zjSTmm(--!)u6(BME0ibR%Cs!<`HwPAm;q(1WZrgmOd^(l96a0%rc zd27Kj`;ct07q`%8{$J4U@}ZOWAkSZ-w|kM|S&Q0UI18nHaT7<6E1a>Q;fn2MPGiCS z2++nfgZN!95F~h_w4wSK2-lOIn+Ihb8tm3RDM>Mt}t^p06|cal}Ku zEA}*rot8h^lfsSjdn7*qvqZsitEy$=VwRW8f}B^Lyu;+UoUo3m{1qj0P06b0+$>Iwk!&Rd`{cHQPwCw8tQNZ={pkeMo*@UpjKL+~0^S@g56KTdbihAQwzhiBZ3|hDm(3HjGk7_bN=&>W$042+gj0 zGf?gH^n`YS5&%@AqH1|#y z%CUvwS!d@L5~0AC6mC6&+UkVo6#UTh++l-OpNpSPub*%Y*}sY?Su->H)%W_L9)WZa z#nU36jt@zzETl$lH+`IYWMHN)l1Q zTYE@klo?m8U*NNQlca04cXHy-eDO4YA1U9z{8^wMd+Q*t-&&DTdiVNfwVRbd(*2d& zTl`AKB+c~gMo?+PWbkj}OWc}iwj?f4B{2Y{l0z4XY4I1x3mX4=XNgCb#dT9-)pMb3 zumKu^0csb;IgQeF8w`^D9@IRLRN?m)zge%apdDSW8kn93H3b;cAk!M=CV-Ycc7@MU zw8j<5+G(uuV1n)ohBj=b=UqlnYj95xRrcaP+gg*KD|_tnL_w>Tf05{0v3(Amgv&LB zguPm~qwb()prm83-g?UDJ65S*;c{KoggySyE*%sit~SEO>)QnYROKanp6esQ-%O?s zmP~P|P1EK32*paiP%5E>q7aI?kC^~u3UVjNMDI7I5v}dN< zaUh}xF!W#Pj%2M7U$AtUX|qb8c#?bP=|+Qd6hZgyr--VTiJLqF->NQxK+wj#Z6?V- z?6uLZJNJ$2O2wC89V4|5M%xM1yX!q{UeK<%Sv$X-w>#xbvS8w4Ho!A}oj}hMW0Ivz z(H^fp4iwl(4P{EmJAUAk4BI*8xWQ7MKtzb|Vb5#;~17|=tWLS4sM zRnMQ_Th+Vz5%WFAp(Dre`Q>GAYf!}*;A3WTz$N_AV%xMvXB>pGNK53k`c?E-t`GC7 zuhpqY?2=`uo{4+}^pFATA5579UG2^`j+ecqEhrNMS{af@txuZJ0xDlvM}~gMkHgk_ zdv}gDK?gTCU&);09ISrE@Ae11s6F2eqjbyk&yk~)N$UBL%}KC4=Oi<5B5&h2*tY1j zC=qq-dr9>M3N9IunIFdo8Q#2RU1S*H_~XKJ$w*AAJDjCjK18yQ?5f^*j&19qB+hSl zk^kC%v1D#AsR|{%N*ws&YG|P8*)(nWbx5lQZ=tg=WA-6PSy;ZS?mjWd_&SCCCB{J> z$YQ{tn*iu2``zH5`ZUal>#Z|3&$u{Md=J$u+)2nbPwwstq*w~tbTr%Avq`KQZV&>1_Lh-HF3A>iv_1c zfE)RFI~BzHwe+ttTyu|dAKz`&sJZeel2GX|`Lrtc$0^|MdS8YcH!zTPV==V2T#?qZ7)dl$;^*VR=<<-%$;!GGuf*L%40+;xiy%Di7{2D2sf|{p$l1F;;Tm5s(p+W-m z>J7T&fE9-3(e~XjU7zvlfD&n(ip2qZxLVJkD>i8GJd8o3}qvd2PC zF3A!^ia(xsJWzU?9C0V2tBWKp<$$FdabG$UtV!ts)zLD$q;Yuoh$29z#DTNE{t+MZ zUi0Zi5{bL=^Sy_0P%cVv@yPop4U#bY%s`}%R7p38MZLl{Yz2TMt+*5T>j-<706AZ8 zjaR}w{t>J98$G^fl5r0_M28`=Nyk$Td&YqPHXj_w+iiJz>qBIP@F$Bbt zTxb*r#$q7ZR7f5b3bd<~(hEJ`*jib|5A$Zsc{ytVi3>*M< zmJlcF&VldOgPp@}g+!?}aD;vSuBb8W&rNrYMZ5J#>1z^&fTW^=-cDElDl5wi{oy!; z65ZO(F1tLO6J?gO9fE~81}w?XvMc-%euxMqsSs~6eFR(HycUWp9xuQ06-I;ip$+Sk zEey7o6Nef#Z@H7RcdvISoBT>F#>|WGI&PXnq{OeDwp`4`JVaw8KJt*wEl!?aeffH? z_$z%|*G_v4)6Z@Rv}6zv5bm937p3GqCT)srSdbx($Zz$U`{8=kksQcM!< zxFwfTg6I99K19Y^XkN!Fcy0e!JZ*9;!nowIaP!Fnb|ZLp6cnw*@n!WjwM)i>T7HZ7 zEp1mse^|>psdbL#gx?hV@XFOtfMB}i4Muq_4m-e@S{bRL zHO{$2nsR!INa||}J5lnhS9YR2YJU9>djf{!c>5hv?AvqOGTpp6y6W08*_7+YPrmlp zySg*c0{sYYt<<;AaW_86f?^l%O7cUxO@I>B65YTN)JAFW)y3E>@WIKXumPPkVvU?^=c||mg zvo~9G!pwV%5M8vRORUouj-tO`lQb>ofIsQVRN~ zqMH@!LJhTcdD$gOfJW2mC;t7}s}6(SLMB>r!@@O`ED2i~=xO5yX|VdOm|TV0b>bv;cEUV)2O<#AFfS*u4G>BoHc}}St<^kL$tTcTXT8@c)Ma~c zr-!h^gZG#1%KZD@E(sdxIbrGUR((onzClz?!aGZe2I-;-g9-8X$k^%UyAvWc?Lf;8 z#a_Np`!TLTXFu%D_f{CE-0^d?_Cr9FAWPe?*YwP`O252z+IRpRoS3htJY9K)kN$YQ zHRErwfynL*3f*B$B#F8GdDra8d7`07pv1n*=eX>hAdy!)EL)ivE5+^R<(VjUT=cspfR6%d@El%*@#T|-kX>o_(4k_-%-HU4}4lNWf z6n87`dU8MSIpZ7eJD%tJ#yI2rI{A}i?X}mQd#%0aoY!1yUTdm9yFN0*Cx$ojgI9XY zEq%jl)FVzT9OA>0nJv|Lso|s;QxwwB^Ee?wPY8Leckg&Jm|R2){YVB~SF+g!LxM}L zE5;;SH*NcHFwlvynQE$HH zFV~g&#?Pr=$As2B04hQ<@!n4tfPFPP9g)fHS`!j)HC2Eg&c-_M!a*D3F&&^Hye>gt#NT8)H=~|-g@~n=5*(iCkx zo)p!ESV1D{PY9qdv5>!sg7USMy5+Kk+0jugj3FR&uWENq(P-f|juKUN6BXbvI406f z>5vvmUlph4Pi|9mQ||!CVH92#`wqmmdyWQ4V>TE5Q*CLg1OALfbg8g2P2p1Ebd*Vc z7$F=g30stf!5xjYCcd6a8{Ck-y!H$aMH6q8uQ?YOL-k@55d3q?G3$e8mi_Blq;5G) z=OWzH*2hWH|I)yFdT%!HpB5Bh>elO2^ZDQHk!X>$Wye4o?Zh@0aiV|DM}v>NY*x}@ zbJJ^mY^tUVN;j=<`*|@f;{B}3eU!-^{w&pA2sTLpI(Z5Z|Km|HE~&uYZq1LCm^M+D zGf?mxQ5?6I|93=_vKh71*RPuokzzk8QMf#n5v9T%j6lMTdHvSuSztkf>MJgR&qR0K zf8|8W_)FAgg2VwdyhJ0HSyHe?5X>r>&iqWTL34O0&yvTE<Dkcp zW!~(>lztB7M_+#l==s}e+)(~dsu{|ndy-KzQFm)g53N+G;`b^IF4TU&XF?Bpv6lH- z0`Tfd(ME8ENyT;MyHNq$!`@gCTel^YGV(CuC=rJ_*8b6XCFq^(dD5YWNU&K{Z&P!k zGPfyVlAR?^_nKJ_jxpG`VR)dL7uBmz(>R+{(v0)G$Vy0aL0=>gBHzr2{rbn#b%pow z;Eh}bn`|E8Ex|wrM{}0F;&EnsV}9v~?`$N~vl*vRo=@SgR%T?7L*wsJy;y}?tgtf$rZEDA8QNs^g7c`pH<%(sx5D~ zjVGgp7IV4OeU|>Zze#^7 zn}*TA6!_WsZWRJS55iav!pIL=B1au@2fTo54@aVEi^G0ggh8wkM*kFF0sIwt-3_K~ zt_uEX77L>G(na9q*%qqF?^x);|J8VaI1JTesM^`6M*mgFEN#P~waeUmfp|15n=mQV z4*RX~=Pg|~KZx6Ei*&^!4}vtyKcjE8%w{*2Uks?oaON#L8`5ngoh{PbX-b!GpMFmU zb1X=D`QYG`XWG?OTkvKPj=nB587D+J9I89|a=N1Ou>FX|U9Q6bzuwNv5tbrL?wK)h zVDn&Voa~NHf4#lJIgwvH8y{BJ+I6{J&NX3U-JQLjCBOpx5i$!qTh4g1rOk|PCNRaf z5p_mDPxuhm=cBxa^;zF-AbDDj3YkT5_|I{&om_E6X2=uq2O;~Higj#Y0c^DKq@Moo zG0|YSqjwlCSrh)TSBA&DfaAdC;xR%EE!WDpe`wW53N|9>D=BLq-HS@ZXT*Z@QNzKY zxRl4|O6*G%I1K!|XsRZNy82>MlsxWPy$nRD`2t{K^O+{E&hx!`;qm7AtS3$T|sPETbJBH zk|Ubh$3|~nd*QZrFgzD1dBD2+K6|rCqgAy`m<#KJ(O0+(^8KJrfiU!T*XoYmmK7#s ziPBNUndYP7eV7vHW^({qr#~}}{yp*{E1?l8Qx$J_cDT(aBE!^dih%hH_9sXBkZCg- zs9GsgY5Z;o-y)giAi*eUPRAQbsntmiMKo}0XFOz`cfUCdL7!yoVe8}kGJk}JoIRV# z_*`ax{iB59m#2_7>9`{dGhM0AiI6?{*r%UW2Qr#UpEBpD7V}%^y|nW#1mr*McpnzT z{}dFLkm9(v$8b{$+ew7M>u(a`VF`cr`z5@@aBjAP4L83zY z-~BbSj3R8ZC+=j@C?Ibrtc6y-8KBd9z(5`ZX-T2 zzL+XN=Mh$DHYX-fpj3QBfbzf4p;_X?{7Qnqylfp|zm>S)0MLmhAXmnj`w-*iQEBWM zn;_ny-PF+Pdp-2^pP-)CzLMM$7aLK~R1i}2s#-UU%MqVx8>R7?VY{M)715K%DXyDR`;>M*##m+!r~v{31gG z?WBYT)@%-EI-cytTZy!|dl4buVk3_&^uM@X2M)M1th7a-u8k3vIVk3_UNAW}-*n^}z_4MCCaNj&;_sC#M!5GCo(F8OnX zYuJ0>s;k}REN7y+=pF2b7-v}6JU;2H{rn~9<#_4( zi0N~?XxrqrpIf@Ir;=62fH6LV^ZHnhd$<}~nFl#4HINsbo_|9c2QTN>pS(*xyr4Fj zTzKU~BF}hSrL1spYj|lCkEhXSw4u{l+-??K=W4^kE`R?}Lp34if8)XVA1LmB3)uc| zO!sd9+yCAM{dbG>uYdpJvj0}3|C22LZ|osGv{&P|Md^8iYMD#!lCaxP`1V7|Dr5;7>a-d-#Ck5=vD zZrANQW*f6szFYO*qU-LB4V%Jfs>~tXb5}F*5U5VmWO;UVpCIQ|gCn~}_i_o#*tlI0@1;lwm#sw3AJhrEbRWa)@4UD>X^l*rCC zc)?v=Aq9W1q z>2p|(a4sn-NW^{rQR6c3$V-__LoUqK!pJ82S{k&4k<1v|f$_VN;kE-4Ao;?kG@fo_ zU(K{bmk@f9`Cci!W}B>p_mf?V$%ovi%3q5wd~|GU8mDds7rA7=a=z@s>3pPtd2+kE z_WtlU-Deg~ohPZ6mV5nK0biJfI!@E}Jml=S_?vWuYP@;yY!UsXF7J)?*71NnZ-SRZ zp(+&DkBx!+#bL-(Xd1hVoWU?SF`I{3E-49mD98?VHI56>Rot<1Hf7G`El3P*6#PV^ zSrGKeGLyZ1J?=IdeH51x?HMgwBuO`p-B16m zu&LvLcN3(C%-+^z4d$2gyM39(q4J1eB5b6-$-sZoDjwYtUr^GvN-FY?s5*OQHW#Pq z9Y&@$&-X1oevf28SA6NZP8}~Qf5~z7&Y2FPffH=f){x&nP)?mrVFHdbh)u_w>K3!j zzVHesX6zc~tai-jlHUT2?Nj1~zhl{e;dn4R7R`mip?RVc@nqaEr={%*H`n|dAB4pd z%E8XW#-vGE$|iDF4$sK~*ZnlLsks!7Q2vI~I~NMT0t*(hIx`wj(?K-i;Zgy@W~kkI zy3Ounea5@DyZu%scK6-}4dJdds3_K|@KQQFT(z>_!Kje=7U=LXMei}Y!{fy?IW~OS zMyByjd{l3>oSYdD^so<$DjL@(+yT%=1CCKqY|qosINUHl2~5b%*FAJ0T%|!g=_;k4 zn{;ml2xbYbZPf0hrP{s{L4{jQ_&ymT>BqFJnj8To*TBKg$X~Ul6|0Z`B}Dx;!>uQ& zpq`UNw%VtyCJM>a^emhAvjLxeZ)H-6TYz_T8QApdOnu zOC>L7+U56NtWE*Og5;iRlp`L^Rn8KCoaBPp4%o3@JrE{T#1nxpfi+To8N>49Ex%`T zFrrNa=J*P*gY$=08`kpu4JI|z;89NmCeU^9!U)~r#<{7tKf=D5z&duM1`+1=6hQyU zFw0f+6%@JqCusl|t^|O~WMJr3?5O+$yq&PjqDaenOK`A3s|!P3p&4&%4?=ulLebaW zFw%k0Vuzzb?LvdzG#6zqJ4+E5-xfp7hrcE;IN+0elu`{}=96V^IafE;G~4E>_Da;U z?Y(ml_sy!8F=OU~db{umI>Np2WHE=scKn|F254P#UQ7$i&}Hxc-Z1Z7V4f`1dm|Bv z3*Kt;w7cv=#T24HSf2a->#o4sD3@-!P4w> zWn-E_?4*BD{#!YOqGt||97m1AYQI7oeACLPG@<)UXcH zxW6$i+agQ9eceX6*6I0f1;k17GsSU}Q$)x{83Wl~@mtUCut}67N0tet?gY{>fd~Gq zj++0d@(pB{r-DL1hT0H6omqmZ;sZV84<@mbrkChzP}KD5mAUXN-~q%{*Na2 zAt-ke1b(V{w@8G-u$^w`=pRXa55eF?=6_1sRQ~bl-=dr#*pW0!`Zkq+75`IXphxwI z`MB?Cyx)n?B;8s+sVoW&Bk4aTa8oG#t|;T7uvp6eC`mTGJ%s||10HC(pHi?z(h64I zDX|7k_O)gi;z|2IU%`5N77h1jLd5EV@Oi|UBnF=qDZ>`CwRb!mS6DsdTc-eA{?%i$S4M_(NNpR-X)#RwJl7g zoOSNK;hTm19ZQ|IIT+TpRS%F6U3q74Vi?<&s}uU-@4WB};+F$THic>RZW^^CvEFFY zw$n%6Pa%hU)4}ww2Qfe=qBwADxw>34*;l&&{5X0P#f3C_Z+qgNoWu)5HPDOb4-BtOn*_ zzu=ih;=Xv^FXn^cmn}8a$Z{80v>}0)a8|rZhUMd@;ufkBi^qN4KO_n-VBS@KK3kvDq^6sl4hfZ&X9C?zEbxN-!6F3Y_7XTR@j1nTof{@em zr6ysaM2he+#6b-e%4Kt79`i1h#(j#1G!FW0qTisGM1e-kTqJzSdGjwVPmCS5Wd_QTcTj98 zD#JA$_xV@t9#)+Xr0=|~@ZNomQ2VAngZSlG`Z>An=|cTZ(vw&640B;(>9}8aw>JoW zcFSqrf3zIHpx-)JaN?$e6-#KDmyx}dS1vTC9I`uaLtGikpvESAasp1$V1^Z$GhOCV zK_M!b;1!47<_OzjZI4c~pllZg zw@j+sZDWpqzwtM6<6q;$7{B}Xd;eYH8Yv?~E<>8fmwP_1s0??!+|IW&MQ%B}RAQ8W z;ZOEeg}8UJ}wyqyqCLE#TpVF0ToPb zN2AaUH6{9Mg9^U3ql3kUG~qpg-8KgKdbbM|XCO7=|6zIkk1+G!nS=j1WBDKO+tq2KrAj*zW2X^M&qCf& zN`YE%#dFhE>Y8$TUMw@3Evel9kn!lad}M$D^q5hn+Yn1kHJPDwZ8D!)q%XT3EXkyU zN~n4l7<2CK4-EO~R%ylu-ARIS@e)Vg5dmmFyCu*#VW0exmry>at2NV6e*yH^0(=$< z7C(Ak_q5?18b;ky5+olA`M^IiOMS^?)r$lwQi=VbxL3x5k8}s^cKg6-U-1XifKDob z$4m? z`N7oc*ccp)N=c_!iZ+em#Bz4gCSZ@(|jDG5R01 z%Qh0?#{Z5f3Qm7s@j{Stm7#1bHFS0aDdJ5dQ{uA1uT5tzmc)SnONF6%hTP(>B@c@@ z+1=;(Ryo;_3i=AoQWaPTaY9SXG|Qkcv2DV5bs4jx>|n!>oOxRpnzt&;V*&{ShpePG zSs;e+j!lm|rg}sZF&bRHuuMbHQhcQHO#%beDrB+jEj8=Zl;-KOU}0~;O0NH zeSP-`J0HYBA4YzBeT+Jp9B;UHjD4xBlD5AP(j|PLPb6Z zgpH;v;pltWuK<6(9C4z3WKWKzXoeS=;$&9{^84>S@mbZ(x?l^jQn{q!pbzRdrR`xH zyb#?{R&U|Bd@A1zQt*F!h zgw9_=*v6DvB?0HD3 zX*UMqcPP&a{YK0ot=t%bS<@VtLBwwni3LVzodU#Tv*(u3w3!4cQ4?wny6m2N41)0{ zz#aTqUJ?0jDn-E-od(bVhUA{Ml!6UQsouHNQk>`F_?!bkrM;kbTFvo6mwl+piIE+; zKfAGBP6%1b%9JQ|j1d_Sel`{#Kn(_nm!(hTY__51Ml7|=n-R?R?fwQB#duH9*FT^K zEqV@syjSe;!(VM;`zHGV5=c7Z#N zk=C%7u3BDVAAh(99h=+W+U$OkK8(3@B()mhJ`$sjn_?sTRo?ae=m?F|@|`^CyzFq+e zY9LLjTX|>e0eIB~#M@9I82kDKVJ1H)Tcg{ZL&+#0A#3Z8_1P*iQ+1qYobT2CfJF1U zk3d=VAH&$mxi|^#k7tP3{pz(IG+f9~l@F?i7Qd>sm}UMbxA~IfhZ62d9IbV&7m@G@ z<`$as+n0iH6>lQwLb}t080hPy|NUTtShg58+WLx{-vfwm9se-+XFLAwiM3 zTV>qUFjr73#62B?T#|d)oX2cn9T&T~o|QYfKF^5`29{9*OPQRqWbYoS? zB-=jUoMlkL<|6MN{~zbu{!h`(fAGqGn_>Gm|Nf)dX(vZ)qmknDCdd58wJ-k*`TX~* zczy+peaPJUy!6?$E$ff-gVwQbN9l~)iyIFVdp0H|o5}MnH{MTpdRv#lJ9#RcRsP>k zg~XSp`F%C2HlDW}6FX-aJyk0|I|T)O8)GKDjOb;(^W@k_Hk59LtBY_-GgE#jP`6=W zy*_U{xP}$f`ie3MoTdMh3Hh_jBhd)d*DWXV#i)3D%LNaWi*K6UAXfTtl98pI&(4b@ zC@2`*6QKcsbn}#YrN6$#%Dzv{TV1B0>il%jJF7K3+~40nJX~cF`AvRx=}Crb*>oZg zEz2L35i}xknbyp&lbTptFmS@mrkt-m2S!@@a23yJ&eiu*mXWepL<^IN<&1=s)~brKY0eQ?G~IE*@Ky86l_CAzRd`8rV(4I!Vdm=R)W>fs zJ|a>O*&CJI%>$p~nmo>fCv&n``plZBJnN}T%8COHAgyODV!Kb%Ve=_s1?(%#$Ihhd z>5`|S|B$IQ?1fF;ZRb{(qopdZm;~{G<@};dHl>OC1af=rH^#)=9`38*<3pqI_$}|+ znZX+8&!x!4t|7-P0=L)3^3tcu)s0N;85<`wq{Ho#>ug!QUb<|OA;R@+3T6;D-|Ecfr1zL{Y3Fu(mED{!OO>}YYAFI{j%TK6$vQm+s^lZL-OPEC zG?y-I&&S`-nQQ=icmll<2xO1kQ*?0x>L6suXDIb76qOe$8xXXO27cb+7V`Z6U!>&% zZnPai;IVUvh!vh}w%Iw>wTCGAxu=gvypm-ct>m*)U=Xt`p9{05xJV(R8G2ZaOOx-l zBbY|R=?3>j(2*FpM9FC9{V~n3xH$1Zt<@bwQ6n{qaOL*Y40~tKnwt8XG;8LdI-V1a z8kTrmQw&6e*l{<<8zSBiwF3nDQT1WbnCTXAoNUSVLH>LhZfMe(o>a=bauU0PM<9Jg zGh*qz!RuO7BD<;T9pm$rW+DUmtQM~`OC<2diZ<64`>8`oP8{bbeFu)*_CJtwDa5NW$5~*qGZ(Sri-h6B>c42h>Dm?sZBUTzH_eSKe6y=1f~ z-2%fz*0+UpAZ(QmD2{%vWhl=0@l`(J+gB7TwQvw=A(WngdZG`))*0txa&^@|b6MoF zn|o6R{f86y6$1^0U)J0kV8mn_rP4{CtBuH11|~9j#yPG+@PZzS~uzl*`Q(8z&%KLtg2 zv8ZwoM0n`S*C2%}@0t4tp?GD>6T%6Xxu5wuI@N3NA)W>%rP{L;Z-te~wUbv;ctDtf zDJLU!9f$mvZSg2nIh925BV>qSea|`r#pOi&LbUJ{#U!sU!pPKfI4U~8{`^T`+nXSL$mPfaIqN!QX_B|+9LxCLa1dD;hpjpj!5tR>| zsFnQBpkjEb>#jXVTNBgfJE)w=z0s~u>bNp{wzk-30^whb6saV{PuS!yI}4{ek?$A2 zOqR&Z7%3_bxO*!|qNucIUZ)anK4XTa7CsT&-T6x-aXyJIyl-@JeRaCouP@2yuzU0T z*mRtIm@5i5+Q^{KSp7H=I0PL{am-38wXEZhzEhx$jk1imV25@rjgM*!~6JK8v zdFSC*rf5%vPdCEF*$=7xy+p zg*Np}ynx@Zf_LVNn#jXdzq059Ep`#r#;99z>Bh-O-!4zkwuP}1CSXMp`86-ZC4N|D zZ-O61r@ZDq%@bKAL{8aB#x$Jk9W-`)ZfO+nhoazGN|p(Vnlc$U-Mar?5Ut#KC%8S# zDQYk`yGq2Z*-Z8&)&U#)%8BgFj|nwX}D~Rv~ol1hy{i_6I>oHd89??U_kqQT_xzt`^f08Ejk5KE6SSXehqXDzs zqHg+RN&l{qBiK7-6Zazol|J++`oVH7sjvS0gBab-uedlFOFJv*m`RDG{4WV49TtDY z*mI?9IQrglHFR5%$NhKy|ulcF3Ke2pnC#E z2V9>Jn+-hPd#!H_>z#!*oj2R)#`@iZ`+4ZDKm5H2noo)af>+i@Yrf@E;1no0P-$>@ zih2!soAD?ph9AxuI&O4fp#9cL5kvwH)Hx8tAImW}{}>tqd< z{Ihd4l z#`4qYF=sv<;f*P|GuZcRFIMU8@FdOpBO7%G_R6_m&~Tq2a~l3Qr=-NUe$CRT_%Es< zB^tRniq#WyhN7MQiIbgiB(*!S5JHisC$innC>C_H#mcGsT}Xhp&^jotnPs^myI@N7 z(mG~p#y)S-XFB;uPt@7D_WpQ|t#Ru0(c(eKOsVt>J= zlRsbNT^y}lge^TlvP3u)A8~*5JIAT)|AE5|+Y?ngGexKsN(A@htKYJcVTMyRico4) z58bzx=Nbj&jx`Y#6+~)#6BDj!g}{I=O1Tyn2q5*ueL<=nP z9}unePjD>r#C{Z-!uCwqq!PU*DbkngBYsW0u+#AFC11=`E^+fCUG~W$tH$s!OH$hI-uhH)nHa+a_-~ zhw0e2diw|FZydayX$%Y~SIw!H`ia?&Kk&^{y}_+xjTN%saNMhk^DxU<&&A9v?_%OV zx-WYll=)&yb|B*JfI;b=C?;K3A3F`WV5j}+tch54$a5g-Dxmwx&>ZsQsMdMO8=l~~ zB{zcgd26%_L8?gcv`|jHERgH%xW*qIC_l~f&RUQf1q{R!kWq=@`1V{n&ZXtAu5i4q zEE*`NX|ubz9xIH{Mw}yB8}Em)9s6dK`Jf-k+TY5Q*yd-Up-xVr;Rb4DjX=oc?uSpP z6j~7_XQp8d0-Nx9ZmOkdLgMwchm?x9BP|p9b3DOu>KjcV zQk>9Md}xmPD&jL ziejY+dY;$KEkbp7+D50QQB&U5Vk+|1MOWc8?caxg8^&NZXan;3rO zFl1nu`*5|_d1C!j5w3QS!mM9pEs~pmF;qfo60qhW3|29_tekfEp^R7ctIw(I1%2ej z`s(+gu=j7_5z*%+)yX0L65N!Iyn?%5VjrJqWD0$h6W5Vc)@rw@eD?!HgS%7tR7{KQ z@Tl9YG4bi29or9B&?GKSOsdK!x`NG`vgQFg%WkvgVP?ocr9Pp$Yu=+*6Y~FXor4VT z%1Ok{2&U&Bs5uEpm_;{TNut+WTMbgluT}Xy1o48Ze>Txkhts%J3)n+WIvw%AZPie| z%BU8xwDDT@4-DTk|B`fdCcKyI8aoJ+m9$ZSg}~80=T*Lu&*f?`eA){oWe5{Od2Tzb z={V?@Ms^1175(`NKss_}8IF07!FMc%pdtzNnq~%7EZdy4Dh}-+g+R-xXhV(0=zKJ` zStu5rjyp4e{_C{32%I}wY}r#xvB7osyyHdq6y>$#iah67J4N}#D~Z+vP1ce8+d4i4 zxURb;5w|DvimCl$*ZhFzDhAGH@m!=8;ixfs*~!&%{z8LGStAp^EXLC+n2Ho3+s~4f z%o`$h`LX3x2(1d%nhA_nSPYa=UJhUu{~}Mc)TTRY4OZ@N9>jcPDGJC>kPr#Ks9O9nJ*;L=k z-=JkB4mR?SV)KC^2$JFx0P$hf9GOH)71q3j$I112(X z9%rIMWk0M;4uzan(y%|iLieE_S*zD;v0ttQ46f3OpA|i&|4`42_9{0r=!_cs3+JW* z7xrH7xze6A0B+GArI!dIbgIzgcvjqKBbfR_sccI86MR_5iPm0qMqU-eQQ%axX!(1Q z>8rcYiJ7A3I~#ue^~{s+#*zT&?!m0)8&^Wz~NY5@Q(zQxPsK>TAl z#Al1gZ~grtKZvEfyWTTD;Z85Is*@MO34=^Iv_76Tx zl#N&le)QB~U`q=2Zo02+`_<1q?jH(*O_QAw?`X4CyM9fwePZue0Rtpllf~sV3pl+t zp_W%Rl0T*1{jtMK0!;_@avTi?aWDLmA@mYre;qm(yWvB>-g`0-DeC*s+@6(PQ$Exm z7#J8IVGDidrHKb_#8|)_&}?oi7zTd6*7gpMK>&zPkCNj+wm?51d9ip)1O4;Vw-l>V z+oskV*{wbF2^jG?6P)Omcm~*^nybez_pmrZkC?YDgLi(Ezss<>v4bakq{&n)V!86q z6WtFw0ur^SKnMxT^wz3jraG9>=8Bi6A#bSJUV1M?zhV(3ho;=35t#UA!q|{{xcTJG zp|&mwbo|J*GzrA7%FiJXNGf{Q%|*$?g!ah^H?}mrbwZy~mFZ0G`{ZxW$=2V`42DR7 zzllB8n&^lIQodqS4pCNO{8SSY2yvi@{Dew!Sl6-{J9osPbZPRk0+N7E)=06C1op&4 z3)-kRtz^KxCm*mRsi|6`emEv(zPOSu6@M#~{%pJFzk>%GQ0tD(Yv(2v5{-&4d~% zIH|%lyA6d7Q-~M}QR*5LTA&vUP!NOU@`Y;zYi}>|XWR!OAP(8dgy5&FCZzJp!7a)z zVI5Z|dD`4C@N6U)n5uPDas8@8bfm{K(c_smk(h7}8@BjF&}2Z*&+V5|v}TGQvfa^v zPZRdK+*u7{uoR;SjGmG~-a=vUu0V(gG+rELCX2-QG&i+VLSI-?nnZx2FAqKjge_ll zdCEetpn|WQ8)WI86=P6MPuR3q=4Oo6%aQr!)b|1N3kV4YSh$QaDim1zz6A`7Gl;{e zj)7u_yE_lu^&EsDRQrdbS7_|;+rQZEIwT}*;f`o%aU&B6*HS_~LbExwDnpMqd$FY# zMj(1(Neu3fz+7g7R3(?&ySCduw3JH-#3JC$_h-)xa80zQS}hRCHyQ}SZ8ypyB=-Ux z?PVxh*jpM9OrR0iQ~F(eOHS>jc=3y7K?qcU8p1{PUZ2dmqW&rT4KuVEmZO3@pOU2& zHKXN1j_W9ZY1GoeQ)i!o8|h(bj}i7QU|Gi};tl%mZ(nh|)v3_PT3Zu`{4kKKq=i3* zY0^dSV}Fq(Ks^kd_nsmUB7}wC-ZQiG1xPA%5qu<#sf~r?L~fwdlYjqd`=0)j9~0x) zuzFu;-t1A*AQo9_Eiotg2A7~Q$NEz5cdjHXnny)k@kqDEXHTu}D1*QuZulyui9Nlm zptzjw5;Q2QiD0wmePO|iny1?k-r1opEeaO#LZ?hT@W9S*22ZVMc{*zgu}V`P&8wC7 zHJJSCfLdh_ZNmi_xeFs90PYEhqI{#D#L0>J&%PO!bf5wO7_1m{cNG|p0fzjdxjLXF zOJex|gQ3$>qG^Mrdgj%hi_y})f4iK|aaA9U7Umo!XWW|zI!Ne{a@uUVf>s9Qb=8K) zYm}S+wxz%V^-P4YJ4)UB^wfn8)68&dSWS5nP)Un{r4)1@I8Yr1~#+{UFfz^A=ZESVqw zlNyuJ?07Fv8y6S%zLiQ5+*aTa$HK+v;V`lCUWL6HL$~JnADuGmkhx+8+w*i!_SxTq zzm6Fhy6jXP)WWLA*UtoGKlHl{Hf0F(ddACpjNG@-j|3{PJF$Zcv#9_yDHuSK+K)i) zw|B>;UNN{~GF189%`3dTMt+xSw^f0=7cwKE! ze7)4dVC4jM!c~eVGNBsj9cnyeyn5KJFOXIP3IS#k8|uipL_0TxCTk_QO3eA8ScL^e z3K?YU0%jttVi0SSNplE48SS?X3C+kU zqGzWuiN#JXAEaw7DmNHd#&y|`YCP@cJN>AC_Osr3z%*_q?(g}b(%i#2^6*v`7ERJC zZRz7RWp_hL1x9QZ#l9G$F+!PWpfJ1f%ct5A(4Y9a9%82?M3n;6ZBK{Utx$?rihf~* zQF^hFPEmEG`<>-VyH=hV~-vJRNiUWWF2FL6ziAWM~768@E|hOLmho29N|8 zFHF3OP9v_0lQsZ>YZL~2PWKPdo|bV^z4Lj9Eg~!~Vt9_CA?K#GO(jl)&|N)a$iLs6 zTI;W>CdVByqTUR~H2Y;^_?3Xo3S=US4CAK$%o_xkYg%9oN@^-C}Rd&TCHR0uYWJX)fOadK&Q&^o7A&gI{oT~G?rej(ARI@ig+r75j(@-2-p zY}q1sMvQkFJik8~Mckxo^GdU+ByZKM+wfwV$Y7dqHDRa7bddN1KXoflJapNNp{m_x zbzSN zalXVB)?vfbx8!inY65ufiBgNE%{UoWO!ZrCl8VH)|57`;>y*5&E-zATaUlAkYoO-P zn|5cKpjJD=*`61%+4!gi8z3G;g0UPC`B$hPQiMf0&A$bu}pW zJ$zp~r5bI5A0@-CY1DnqXp=cAQ%Uo2*iq8c`X<8_{=oNK_T(4iy) z*-4y(#DJQS7bUxYT>G(c$>JuiOPsO0_q3$!LN8YHNu3R9#6!Nd`!(1Gwj8&lAYxD`7~R`l(}XW z>`aHpO*fx;LWSvK_~z_=3IST}T^X)gz>Q!6x@pJLc6W@8)5&BKFf9OFG6E-hMJ zTpPyXLIU;F$;3a|th&+upm!eg5QNjQpXYj4k(vxvtd{^n6_K3;S6e7^1kGOi?-gjmNW;*k{ zrwSylapKSsp%}eyLMIsZcVg*xox0ha_IuaQ$s|717M{JS?z`pCsc7m_k{K}ycIVT6!|al?YpgEFGMXc1tBDEI)qHOZ zwd7P9inZUC)edQ9v*|0lU}dGmz8Rji+JC%U8oQZ`O|cDt^H$J1U1Q zG-vI_N0H~$1Yr7|G2IH4vgpE<0MEzO%(djBiRy^k2rko;KGKKK6UKVqna zJZ~@l=8efdCHl@l9}GKt*+-?DK-Zf|Q-@K#Y(Lj+%kra{O=~-p@M78^ z=YH?V;OjmwPh)a$;y$Lmz<@<7WuP37J_lPDSIlQq)|&6;A#d8G`JZT!QdCSCS2^UM z_Wql7nSbr)`8dw-?s$EWF^fqVktpBC>i`MhkKM7?yJ=u`sd-xX`wvbryp&JSRZp=t z?45YVJv24+JS{KL-`c2g=Tv-fyVGiWP&@D4XnWvS626#%S?c%0)+{KXQJB+mL2$&K zzw>i!t>@ z4GS0N{J)OOh*q|<3VNA*#P#JU^IC1rD=%yQ5audrb=Hq8eC(`HDw@@M26iBau^|G+%kMoHwx8q9!auzx?rynO*C?y=3deKO_+KYVh(szz!zhz+Q`y zC_i$cAg4LXAXEU8chYM4aS$Ajtdgv$zL)rfY@N}xYQ2YVK0|~V`5IqW9}@cORcSnD z#h540HZ7i#ec3;?weORP`<-|iX4&LSvfu6BDjZ1dAFnWU zh#)mqw^5Vjdj3w%-ab^VtSmUOKpAOTqePg@)W2a7bMJ|Tl2ka!i`#iS zOQLS_=CUag61A-ggC(lHMUklM#8;=-g0EoWpRhC@r5vE*b~kss;bDaI>$QjGI~N6* zQkUL*uc&1s37Mcva}9Mw3(r?aPIYkUJ%|>6=$3UyP*hV}T7txX&$wl&CLfK}oH`ge zYjc*N8BX&M-D~8QFz&}XLsAImtLe0Md7=x9UWD-Y7Fe5zAR&K~P`A{aLO2lu zHr3G%`CYcFp*r?(cL~{c`+oC@R^+G}Uk_~uH4uIg*8uT{{T)>H;fEjvhN7o;Cn`T4 zO-@!o>w32j5bmIk1VOzHO~Uo2oB5}Do{gXRQB>i5@+I$0Trkn35s{zw@5CEuQ1v+j?S0o2O;6=yO)-7K-^#(Asml-vb79g5 z`SWJ&&?}~%lF?8zEElj*i989^^}|T4J%Hbdh`gv1C(tx(7b(nazi}XD|A0L zL({0XdvR6ytCN_;CCWV0%NlZ4b$4QXS$jvks{G|vYqxP`>%l|^#Wyepso?YfzXIJ5 zBJca3_Nzy&sqXzTKa`KG*nr%Y5JsO95Bp&eLPS+3gXnWHAa@tGdln4=0U;c#-|%bN zMfD{jeQqaYa2>+vb0XB|4jt0_U5pFNm&gI3eE8AlEFqI#I^-+)KtjmCggEqv2OlOO zBBhRcBX!a1It-@_b5;ZBbIn_S2$Z_vi;#aZNnam)IHJ9}FG8I9{Fg2sd<&9KcV=f9 zKukis_{g?D4Cn7ayEh7F(Ca2ZZHArq3BdCYl2gV$&ZWj=lLn z9nu-l%d+W@J(>RS70be0$X0iP33f`wgV}62MF!x*rPTdIdwuJ3yHf8ALWonJGkkCW zLAkHZHfI??C?Rqa;$NTJlsYr^#D`MH`b59_gRj&($9<-F8XfpJ@T<@5vNwAa4n&pE7NS-D+EJ7?m z_&P+?{{bGnOrJZHI`i5)=D+{LFCR`qntehzfSBT??(Za}Qbm}+fuYpVwc z-t$06M#2ZHKZ;`gfhCJVr7qu_?p2qA;(55H9>x7X)> zt4_ip-3W%`Tc#EX`;K1;mAY5eiA|pyzWFefI`OB}?e!%|2Olm#rp?w`HL%la!uNFn zL9Zz$CS69?df+7Ke@4{;4J5$JOcAPZgl5%|x| zLJMB7dw^ijNT}3Z^(B0Lp4m9d)f0(&?oFQ`-%8zAeTgqZh)IYGkip|u@~V@Lm;E4Q zAU^z8oeZweeOH}?!)QUii-r_7ezoVH4_7Qp(m~~~zC=Wb<|meg`lBPz=Wak2tmQ|* z2OL^;GQ>ot_|+xY|G`376zcPhH;G!ZSWLzWxigj!n^G4LVyiFF2I9k2b<)8uTvx-? z$jIRV88CijC#2(LL&)v)`Jh!NHaj0Bfd85d5S;_@L$QaUVM5R{DHE zLLBj{X%`=MKukU~%7y~uS8xJr#}Rz_2Laaus$C!mh~0> z6(=9USL%-VRsI+E;puaW4LZ~CKyzy!Q`Evh=H z&2W|$4$8;!dabrDQ|ip%cgn|H`@na7$;-;KXU{4x-Gn%lx(yMWbKShP{_r2m@)^H+ z|KY=XpE|@f_&plhVlefs&j(WK&nIuG&xK0eUCLW!;#X9k%K_=>bCZv6DPcqD>21A# z_v&hE2cXDUaAY2?)oVqM{*SIJwZk?erUN}M%O1Y=fdd)C^ zkYz#D33;B_WwRf1yAz|{{T~({&pv;?ckkZk&(A6&Ql$`^I3+z&L-5|BVPHv5=^_#U^y`>ud;#aTV6goJ1L``<* zp=_MlVp&XrwKR6!8;Ny#Iy08+>kqy@KR;YwKX>v$2@xrEqxrGDyKA{umBaR#A1Bri4-eZDA4iF8 zijPEpyzJva5vfQj7pWCNMkpq;gyln`&m)of(b0p^(Rn4}S#^Sl;0&K2oQN{NVjC2+ z=fMa3&JsdcM@_Sal;Dh#lx@6E=XQKJt4>gSCMP$ahoURVtxpxYz5NHh)<$}Kr1Di%_RQUvA?23pT9dU z>>L-3Ms8zcW5>LE(}{j4%ibSCe;3jzC6mdL>7YpS=jRC(pF>h=toQVX<^}|Hi0v9z z8s5K6bb{=&=45<4yJtV2UrPEzC<(6jXqH;chq5w?(}gOPiG#iQaWAD#jS=aT;h-E~ zuc3+g==E>uG8@8DNJ!bd2GML|igV6J8Tdb^*PsjN_D@0 zQ-8uib4Hf5>cr%tdzh^v9RF(5_|t~Sl0Pp?cv}hi06?f`rKQue9XB6+K#&mP*u7!j zxh?7c$eiVl(G^P?u^sflUT@UX8trNC`&gDz_qqnJemB8re=OErUO(>hv0iJeJNiSU zm+;`D^4SH*y=Sgi)~w+*X?I@C$Jn@0OGx;zb;qyx zB82;Npnv6#U!jJ_aTTWRz`1d$aybKUxcFcIk#9_(d^E)I-9T?pDW=#n8}Dj_ZjL_7oXH4rT_efU=QSoD z*Gw!gpIdws0gc3{Q>h=%dR$|)ElG8_4T#%>e5rht6N32*5g(I}E0vQ;11Ezj^9Svb zk;jt)LWq2$2vOd9=nvaWQ~@FKr3?c9DrD_Mpd;fbEb&k%$P0BjZHW8#xZL9E^G%#q z*&F?{X&5p@u(nq^iCV(Px7Bn%O7G%>1>|mCI`W!mwVbH`W8+Q2x=N`)Wyi)l^ntV( zYe6U>$Fe&MH4Z0#zGK_VP`H17-0AZHUu)XZA>e*Vw=x*b2?-xBIR?aSLU3Y`^cmR` z+YjL5k+Bl_{pilo@1G*6$3Td{hDfKigN^d!V^4|9Uq}i0u%dbFOW-IAbEdVf(Cwa~ zEyLg%k3LOh*)=ZR^WnNy0WV@vfd<8RA|Zqj=~|zuU&MgqOZ{=}ET zz()oNv34`W>R@F+gy7zUV&k3TJ2qrX4e45EPQE;0TYz@FUNG&4Rx|lXJ4MTmDqSA* zs4}1d0YF?C!{JF8=$bMv;^X5;<-kyWHwPa6el&jZc%<@(uhhjGIqA~2poov|p#2MQ z!QRj7y-f|=WQiu(cBbtGx%t7Mj+0!1bXZ)Mlwe2+@i`H$jkW?&xq-m{g=;V1}%7nVB*sWe%1Akk9Uv`dcyRnEn>< zk@`3?d|<|&j=+R`82>P?OpFXq3T23d!S^2eT*u2Cjqgd7`umg&5QRSg3)iy6k)2RV zH_9Q@B=Pdr#n#?RhzC?qY^KQ7mM8(Tv}vv;F~7c~GavA9+7dfi`h{4aSM$@VrgvTV zOCZGVVA%m-I@R42Vtk4pz^c~@Z(eVlbqP@$$H#Q;9OglskA5vNA-XZT&Z{X&umk6d zjpH64=f?m)I%V)4>t-cX@lt2wt=NFP{Oz~Dqy7N-(iSTnt)mG2D;)Q0#%6zyjDPs> z>FDU2gW-`AzNA;gCPbSZ_lWQ6=4HC;N(4&%C_;pbqlv(PU_(_fY)SPv?uw3=d#mDk zl3uKI#0I2C2tHtG$ucN^ZZ3&T2ovk|Z9uBi+i6^pi>4OHN!2s3ANaa%fIBg7!ZQo6CjF*zF>iCK{! zXG|veMZ3q0kJU&tOnnYm6yUuMcxC4a_!(I1QxL5^7pDj*9ws1J7a|-$MovT!5+aZk zEFZrg9gUBFqPOg)N5dl@6+tpY2q8B`$msJP9~VkQktp>eMRfOSosg(Pk){Q4JsTAw z>_lQPAv{qp0>ooP+>#`rL%#!>8XrnS6jyo)|gd*PMuN zUM=X;e(X`DZU~k7kuVgXm4ng|ao|?z6roVr3lK#J$P;P4d}(uYb8!(jOf7Z|%9a=q z(QA8J07yQ`wt>##J*E}6tKu*(9MN`lkRbIl-i+$k8}%45Yj;71bd|b>)UMBvr!Hq&p6*Q+!O(_ zrwH{qF;;M2!qJDw_^02Gy4yQWMke@xL^_%%zVv|?w4Jx>5BT`PRO)8p@PbE3uq?X9 ziVP6C|CTyb;A?pSBHE2hJOHtxSx+`;5@bJQm|Z|3kcQcSWm2{J{*0D1S65&Vi6f2 zd!n1S%mbt=(D!uL2$A9xrjEnSbW(ces$*pqC54uQ>k;L{fAosVmeshvzdToX}VA((!UB2 z>R$mL`kzP$t}0wy{I38~t>#r5AdNsh_a9dt9Rc}&0t^85-wV#7O22Uc0000 +

+ « + 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/108/adminmenu/tpl/orders.tpl b/version/108/adminmenu/tpl/orders.tpl new file mode 100644 index 0000000..4ec7af3 --- /dev/null +++ b/version/108/adminmenu/tpl/orders.tpl @@ -0,0 +1,135 @@ + +{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} + + +
+
+

Export:

+
+ +
+ + + + + +
+
+ +{/if} + diff --git a/version/108/adminmenu/tpl/paymentmethods.tpl b/version/108/adminmenu/tpl/paymentmethods.tpl new file mode 100644 index 0000000..4c37e02 --- /dev/null +++ b/version/108/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/108/class/ExclusiveLock.php b/version/108/class/ExclusiveLock.php new file mode 100644 index 0000000..834e00f --- /dev/null +++ b/version/108/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/108/class/Helper.php b/version/108/class/Helper.php new file mode 100644 index 0000000..8f4772c --- /dev/null +++ b/version/108/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/108/class/Model/AbstractModel.php b/version/108/class/Model/AbstractModel.php new file mode 100644 index 0000000..5638700 --- /dev/null +++ b/version/108/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/108/class/Mollie.php b/version/108/class/Mollie.php new file mode 100644 index 0000000..39e76e0 --- /dev/null +++ b/version/108/class/Mollie.php @@ -0,0 +1,550 @@ +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; + } + + $logData = '#' . $oBestellung->kBestellung . '§' . $oBestellung->cBestellNr . '$' . $order->id; + + switch ((int)$newStatus) { + case BESTELLUNG_STATUS_VERSANDT: + Mollie::JTLMollie()->doLog('181_sync: Bestellung versandt', $logData, LOGLEVEL_DEBUG); + $options['lines'] = []; + break; + case BESTELLUNG_STATUS_TEILVERSANDT: + $lines = []; + foreach ($order->lines as $i => $line) { + if ($line->totalAmount->value > 0.0) + if (($quantity = Mollie::getBestellPosSent($line->sku, $oBestellung)) !== false && ($quantity - $line->quantityShipped) > 0) { + $x = min($quantity - $line->quantityShipped, $line->shippableQuantity); + if ($x > 0) { + $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', $logData, LOGLEVEL_DEBUG); + if (count($lines)) { + $options['lines'] = $lines; + } + break; + case BESTELLUNG_STATUS_STORNO: + Mollie::JTLMollie()->doLog('181_sync: Bestellung storniert', $logData, LOGLEVEL_DEBUG); + $options = null; + break; + case BESTELLUNG_STATUS_BEZAHLT: + case BESTELLUNG_STATUS_IN_BEARBEITUNG: + case BESTELLUNG_STATUS_OFFEN: + // NOTHING TO DO! + break; + default: + Mollie::JTLMollie()->doLog('181_sync: Bestellungstatus unbekannt: ' . $newStatus . '/' . $oBestellung->cStatus, $logData, 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; + } + + /** + * + */ + public static function fixZahlungsarten() + { + $kPlugin = Helper::oPlugin()->kPlugin; + if ((int)$kPlugin) { + $test1 = 'kPlugin_%_mollie%'; + $test2 = 'kPlugin_' . $kPlugin . '_mollie%'; + $conflicted_arr = Shop::DB()->executeQueryPrepared("SELECT kZahlungsart, cName, cModulId FROM `tzahlungsart` WHERE cModulId LIKE :test1 AND cModulId NOT LIKE :test2", [ + ':test1' => $test1, + ':test2' => $test2, + ], 2); + if ($conflicted_arr && count($conflicted_arr)) { + foreach ($conflicted_arr as $conflicted) { + Shop::DB()->executeQueryPrepared('UPDATE tzahlungsart SET cModulId = :cModulId WHERE kZahlungsart = :kZahlungsart', [ + ':cModulId' => preg_replace('/^kPlugin_\d+_/', 'kPlugin_' . $kPlugin . '_', $conflicted->cModulId), + ':kZahlungsart' => $conflicted->kZahlungsart, + ], 3); + } + } + } + } + + /** + * @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); + + if (isset($order->metadata->originalOrderNumber)) { + Shop::DB()->executeQueryPrepared("INSERT INTO tbestellattribut (kBestellung, cName, cValue) VALUES (:kBestellung, 'mollie_cFakeBestellNr', :orderId1) ON DUPLICATE KEY UPDATE cValue = :orderId2;", [ + ':kBestellung' => $kBestellung, + ':orderId1' => $order->metadata->originalOrderNumber, + ':orderId2' => $order->metadata->originalOrderNumber, + ], 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/108/frontend/131_globalinclude.php b/version/108/frontend/131_globalinclude.php new file mode 100644 index 0000000..5778e21 --- /dev/null +++ b/version/108/frontend/131_globalinclude.php @@ -0,0 +1,120 @@ +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 = 120; + while (!$lock->lock() && $maxWait > 0) { + if (!$logged) { + Mollie::JTLMollie()->doLog("Hook 131: Order currently locked ({$oZahlungSession->cNotifyID})", $logData, LOGLEVEL_DEBUG); + $logged = microtime(true); + } + usleep(1000000); + $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/108/frontend/140_smarty.php b/version/108/frontend/140_smarty.php new file mode 100644 index 0000000..ac119c6 --- /dev/null +++ b/version/108/frontend/140_smarty.php @@ -0,0 +1,78 @@ +oPluginSprachvariableAssoc_arr['error_' . $status]; + pq('#fieldset-payment')->prepend('
' . $text . '
'); + } + + $applePayId = "kPlugin_" . Helper::oPlugin()->kPlugin . "_mollieapplepay"; + if (pq('#' . $applePayId)) { + $selector = 'body'; + if (array_key_exists('isAjax', $_REQUEST)) { + $selector = '#checkout'; + } + + pq($selector)->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/108/frontend/144_notify.php b/version/108/frontend/144_notify.php new file mode 100644 index 0000000..d1826a4 --- /dev/null +++ b/version/108/frontend/144_notify.php @@ -0,0 +1,63 @@ + 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 (trim($oZahlungSession->cNotifyID) === '') { + $oZahlungSession->cNotifyID = $orderId; + Shop::DB()->update('tzahlungsession', 'cZahlungsID', $oZahlungSession->cZahlungsID, $oZahlungSession); + } + + 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/108/frontend/181_sync.php b/version/108/frontend/181_sync.php new file mode 100644 index 0000000..86c610c --- /dev/null +++ b/version/108/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/108/frontend/img/trust_eng.png b/version/108/frontend/img/trust_eng.png new file mode 100644 index 0000000000000000000000000000000000000000..3d0177359de7fa93833765ac05d8b218fa1550f2 GIT binary patch literal 15299 zcmeIZV|Qg;*EJg3wrx~w8x`AjDz;H^#i-b}Z9A##IJ;uoI=S!XxvuwZ?fiiA<$T$D zx3=e+W6Uwf8f%W#d;h7dD1``z2L}QIf+!;`t^xuAy7Bef69($*Q%hT{{p$*u)Lro-*EH{GyLmA_Vsl{8>cfn<3wG+><+V6?QfNT2o! z3JUtuz3p$W2Tj(C27(9arY)muz;qlle}DhOf`S6-=URC7l_o9bl6_2231L)a2#laj zLQsn?32STX#plb3v$M0y!tUHMoQs$HQ(ZPq;zs-RmWKtFTvcyxUUM@ul(_5BY?c5O z$$9s^b-0A zP`b@F>VtOV92Lj^>wZ*UP83MGgNO)d5NL=Nz?F@iJ=e_I!%6#*;1`fdudUAJe66|n zFnm29RJu`HTYHGiRr}mJ=f6w;nnIZt#pssKeyeL$y1UKzAjzvzv-V=6O1m+b1w<5= zsWQQqbu?#us)dLyXkMdSg|e!rg!Z4vrQiis>$c*Jz@wxe*BHe*>1wdnUWX)}C(g6cejnelOF!r1R?{S-CIB1I?YC83 zjh=uwh^panPMc5t-;iJgO~eLXX5)8t4+xMyF9W4Zivv<;1$sU4nxX>p&@-q zU6p?VNyqiy3HzO<9aI5)+A{x1^8X1jTEu^6(~M{M`Li>o<==IvIY8KLRn&~9yz0+& zYW`=mQJRR+8KC~|c2=nKz5V5+a?@#3VmwtM~ofajC&v&c#4kSy(I_Azw-G zITVTiO8Dh6;8X7Ybj4%xf)1K56j-Czo|xnL7Z#7pfnYj|>+7bD#$r5*VV}vXtm%k4 z`BU9lxs(6?zwQBKU}A9D!wvkO%^y$vULV|WFZRZhjvv+?J#LYB_E6G*Tds==cVp!7 z>0KKedM~^2LXljVPX93_W7+1|`_B1gIsS>SGsoxSxHK0gAYR}B2a#Ct_YBV=fl^P- zzZ)`7a`pAUca%ap3ux}>`u;(`&D_SM!l|s z3<$)AeP;z8)7faEd4H0y+6D+!%(3n|L1Z zahn_23bNTL-BG5Bs?%tJsPDC(vQ#94&U?S43j6X&9?!qZg-nJb%O-5|pHRof$MZD}yuvi9v_zDdFMB^fuDMc&GG$EL z60Ag4^js9$H+vt}Y!z^&yROlSb_?TpC#qe^7ByWQzqc!nVQ39 zDbm32Ui}16Uq}m;!l{wYwc!-dC?L5 zRehy4J3UGR-gb#yuZOW!4`;Grfxm0JA2NH7`*{AEJ0r5JEGqI%FGU=qfXy365L)~- z2T!3(2t2($yxi!8Ch{a{;^00davnl0Ar|m?D3W9DPM#5bwX<1kDnG{V%1V-fwQM8y zyZhCip^(Ly;cu#rT%!4hcp^|48X3ZVNK~sFhV+Ohrq9)w{Nt=~cA$Yn0aY3gY@TdSfKS@ZKX{1Cs1KxLCm8++U>aYn>)4(0r&{-rSrQ(Lfn`Q!jz+ZN z4!y_{hKoAeHSt&6`!jch*y%WRuHD%>O)oi&U-$J0{&CxGT%ok0-4XT$VRWq7_W|{Z zG<|nmAaGc&ESfWwK^x5tz1cPknJD81#{J?eGMnvgA^6MPI3!a|Pyl zG#8}E4TSiLpQe^`Quq&;IY}1I6Vv&*7JJ=A58VMt<0=@-*&KTfU(b2F0WvWS(+4V& z=jUhF&-cfy0vrnE@vhg4&XV}gmzCbB3bnPLrJpLA{z6YX;F-K`723lC^N7(oUpCh^ zbSpKgVz6wU$D^osU7jhpfUSf|3+|R@3{@>uG=w~+}tdSnLHk{HR4J496bxFtc`xq<{(_AE^#y=Ukofp!{n>MIT7$6@#JgwM6DO>qK1o1a2hm9S}TjCnKZL zJhOPjDR3OyE4GpDuUwjU{iAIuqq+M6IS9^)qZ;|U&m{~G`$?l(XSoR$DuOUk*hmXU z$T+ke?vg8KJ`CFcM}s0E+=CrY1j;WaJjd$*cQ<{HYqsB#TIWzRhL}b4jVjBbeILt1 zFvZsRV|-$QtV>Ds2zkV6I+J7iDd{fsy}`0Jq*ol(17R`;?sIs7IA@3OLDuDA%g3?Q z?eq^4yJ$pqVS!DsAyR}obqUkSdO(`qkDnne??ar3hePN}cgZQaFq*aG=(T7G*~hP_+KrZTgPgCX17F+7LrWwe7QG!g1XR0=O6 z28T#W{toyHTpB(+ebMJdLHx;}{>B*e?hD9&Qn?SaJq&hqy11RR%=ZmWX80}nV^A!B^3E!rG-a_%-=30M|g zJ%8;U+!m}R&7x<4#{`02Xv6IO9E8~P4o3B#C#r-mE_+ru0PMti5*RQ*ybMG$9DWsQ zuH&ga{`a4*WV4~zFG%p9L7dJ3T}G~enoG|9dCU@mtz(t}U((wY0p!q>l&0gwQoboY zeit@)^aEU{rmpf+4ST}L}&{K*__a)rhXej;8ayoOrPJE9^+=-Ee zMn2s$+%DRg#X2A9YS7G4l^?MBZp0XAyvKmZbC!I#MK8CAWGggP-1BlmAJL3~B)3yS zd_+8R9U+6#mbg2nu-fn;vu)z%9}MpF7txA< z-;v?1PTClDbHfa`+>WAG1-(!ZayTOlb_5j?^_S}rR@F~NgF2|2{JeL9mEs)&|z`oY{9#;Qt zUF#u542)g!d2Bn>V?n*CUgzl2WN6E5$FX@T_98PT8Y5#oOIi{mpV`;i*$i%|et6e(?M}RF<5n5KW37mn$nkz(#M7pIfl#dQW@# zFwCfPc1)OhgD4^MTth>yY?bGwl3pT*rXzgN$@JZM(rm~i72 zuhtngNXO7BpfT=!GpE#!MWvYUS)b&er+^RM^tvF&+`eMMm!>!IW^x_It0n&=^8zFJ z(;rrZK5!Ve$cbhDX6!<@X%_i>!m}T2QmHE_X3$1|(St8{8iFb|TajN~91H{MByC|& zTjVeLKmjZu@EJxmjbD1oeyxf1nP8H6Jp6gWMPUg5g5`NR$K;rPX&{GWH!esNZp%(( zt-1xTxA-Rp#{XlO5sS}mi`5UpVIJ*LVuy`Dy7*(5daYk{)*{uDsBc>d#o+9RQ36RE z%C+=7K`QR|$w&#TEk0}L1uh@to$Ml<;<^^z*g%6W;7vn1%DZS)>BmsC2|T zmh(zWH8LkXpk1VH60V;UX`NuKWwS2??Lhz~M@mvMoTufpUvM0rgl`=zI!P&1E z`3ky9YKb|FBnWP`PIcx$K7^J@kopR*iwfguEC%@&j50P0FTJZ3{K%pcwg3FH8_Pa| zC(1~QJ9T2JSo_8`+iZsaRz7JY8qy=)^$vd*X&3fE0B~%a*5aJ@IO4B(#RO^0?B+SV z7CMLIiwcD?K2Q1G6x?0c&NE&at=+Pd*-n%UaIs73`x~+c!GGX@pDh9q=ho%PCJ}@^ zl8VClFzOHP`B~7sS-|ur6zXYgcb58tsaLx7oqgvM zhunfif_7-22iipBsjyU9cJuh7=L@BLMsj1MxW|H6oWs8tjD*WSF@Q?EdE?f_M)au- zVkKC~tuN-IvO(f5b0_#;SJ` z2K4my=(muKI_<=y^)aI<^i#?g)YgHo4T^mNBZ%dte+8t(qTr{|#*5XP(gCs@K`mV@+?HQY34iv%HS~EB}#el}9 z3tFMGa7zULx_Juopu<6ILQEI0J1>!^0>r7-n6C8K2h_dZ4$kbau6kh(GuiDswi2JR zKKKn=FwVv82$bEf=9Qp)N{-|L#c zJv2KLz$Z1HMx{>fKCty(SHlyyb__MPA-q5EJ(nEaF>k_X*q1x~=BG4rRpeM{m= zhZQxeqq7`MojWB#U8!59ZKy!dwIAQZuCIr|y}lC?Rn0N;DLxh350VUg zK6xMJ3G+_SczKn~dXEFcEV2#(%83yx?Pd99&U;QHcv@DNC6oo+I99YWi@`=jS(Y&` z$ZaOOWgIT^4H1diIMp2}kV~tH)RhtyWA--XivA2DTtWxnOL7@d<=4bv_R&$$(lm-O z0V3u8^o`ETimwQ<3_vP=W)u33eAQ(xC^kkhFw(d42BAvy%HK9VhcH}OgZ5Y;p`?Z@)qtDH5m71%uvG9n$JUTR~stNit9N z2PO7OfLK4w8?-Do+v1BAUNyj2xAMl%K22zRyayEVnA9W;5Q**wT>v z3?~!Q3e?WT5)QpGTFbs`@(ABf20%j`I-GAm+ISjHIC}GD^Z{bp?-Mg@h4YgcEWLXA z&bTqW39=j;k3!liQZvjC6@nJ$5(0(~N5lqNEMjwk>U()ZTJIsRRikiFok~F67{0M=_7BEmYO5dEU0@V=8K^7v) z3Y4*#vB9e9gB#bu!J&-j))26!h!%|bF&sY&+p&DIyno|33H8BEfvpepOEOZ>u>!#D z?-`q*0C%KlLG5D^jD4=cb9NkiktM4?Fav2-*|I zXZegQ6ncX#&8F8NxiUbeXq$AU*1Bsj9zMA}$GyRLlmURc0Y>asx7`pV=I%C8G1Uq4H84Uu>?@%QW zPfQd^++CyT`1^RCv&IXe0sZ)Oge<whZs;#2__e7*+f-|G-6Y~n z&k;DY0)4vQ`u%dC+wgZIUF|gb!Po3F4b3713>63=I&UjBfieT=E~9;&`}={*q!*Hq z46IoRxXRzu{g>qh-<$#>J9k6C*D^UXlFY{FA;|Bu8DL9*w>>+d>_hb_DWPa==bM74{9%` z5VK%7c}$|EWWVlWvn9YJP%g+1 zXa*s^JCinyst~go#q_Gk>Gf(>TZqXb+C%zMVhgQWxx@qUdptmI&jWrQUIxm{Q*y-X zWaFXe@2v>Ta0IC%0>kMdJYB3kx={Fbp}#_*fI3wsoR7@AXx9A3QZa)HdjR1#?#e=i zfNKVp0Fo)>S+HC1zdZBALj{szH}Z2xKA>5wVWywyCdi+W)5eQ1dguXNk+E7eRS2OP z<9acNcs~JL{frU*;iva%AphWQL>p>=A4xOG9?SyATAwVE*I&Zu4yT5oqQDd)jS7G$DkpQ9 zF^Vi{9BoVHaKLokt>-K8pVXpjam%G>Eg7+aK3QUVpAGs8_)5F1I#wQ0**vu1HQWWH zmT#f1K$hORDhu0G z+Ip|^SnX-~#BsL?y)%-}SNKf7S#LgnsdEh3IRdwK)};2V0~fPGm>}b~o$7Yy*4N&r z&xAf>xs+V9HC&5;LU#GVV63UamAQ@KWPmZWBMU*Qh&jWeEH)x$)biC%+j1HNXJXsH zm(g7x(;n@yK24I9p|aqx?0hGh`OZe0s*b#$FBlFawt+Lim12m4#gvEj8{7%eM*!zT z{i;d(Ys#I%-CvK|6AY9KL`C*=mNUrErTTz;{~#A+yCbc;P#!tJzB9ry^@9UsQXfTc zV_5-Uc){^Ivg}*6K6Y-}P-hS%91DhelRnjgvqUcOS{OD94pTbgmU&m9@r>B7mxM^y zDSQ~x0W#d(L1IC)S&qM6Jx(?KuQ3u32f^Z~IjjVE1-PnOt|VLdm*68m%dSk%;pI8l zh{?A!rffglSssQrf*z6Q!Tp0Lh@(kJWAtIz{RcWBUWEp_NfL~VVa6++C?Qf<2rNXN0ug7zNHBo@Zx9T?S`dDqCP+Vm3)c%|64+U{ z3DxaN^;mmZDf)~P{Q_-u=Pe3MR$bC6KA;g87!HaQFY${g(Q8_u{haXq?ZJ$}BgH4g z5$+qgk^hyR=9(WWN_ZB|waw$Oh)+)vN}w20I%5;;-|3E|j)F9G2zMR`5?FFU{HDU+ zupT@p4}D0qgPY*QCpG?Z03;-Qfk2dl1e)fT8vpGzAqf&42ow#Ndq%q``kXknA;ro4nR!ZN>Hb4YSD)=t=kC2Zvcb zPH8!rD?Z~cTdk9;V$+h}1hq+1Jv^SAxyaqrLq+!wO34Qwl+0oKhr8|R3!BPr62z$0 zlF=pdj^q@4r~3ZU@}o9M;E18ZeK9t9q3jo}ZkHFP{p*GEn&2acMP1nbA0NNX3I^aPgt3No8wRYLDfS_{4UDfXOBMf=apRsQ1#T{2z!bXB_)& z19_`br%vsVb_pnhwV!xg6(YpG+-W@-9ZL$MBDFcy#|blAVG92Kq_LlNpnk9CDw!NU zkk$IFDRA(us?BF~P22uFOZ8%YC-dF3ZWl{^i>bdcFzgCfw#SOYqW`GWFQ?Q*oXJ_{ z%WQSqYNK6mQ5!BtAvtObeCzG%$hNgySkmQH*0GVb^5b*fYG7Qt+2ZG; zb$Z)vwm-@%S*3c9Zw_0!YB2;ir^|G*NwULpQj^?$R*RruYN-t%N4SZ&zL;RH?W&mW;ty(S;R#7B`35jwuj zQ1U4Bdw1NnPKjf!);LZE|FPfhni|Tp$5%jyGH-Kqfxg6JlwN@}Et8HVL{A?!-c9NWJqwdCi z?XeR6xSM!IrFN$IY;SHCh#nF%X~R}DCxrKjh#aLk*Wk9!JLN* zuA}U&vj@NRy0xHev0i3F7u(a7!nPhcW-CdC3WM#2<^)N;Ix=qO{Cr1roIIa1oQ3Jz zQ2w@TTg2p;{ev8nF$aUzCthZDW~PWd-b$Wp(}}|G&7ZjA{D4TUQ&D5*V z^%ClKhNn}YxM3({T+lJqBQdOKqeI=kToGA_Gc0^dh&Fk(BkY6sXs#zmX#DqQi>E$K zqr}14YGrv@8F!O`x%8Xsh&PxjL$u^UP3drDaCCLU3BXIquVqRQQDCdCI5J#`*>E>G zQ8-SQJOp<)FIBBqHle&Hvo)yZbK5cBPvS4TuacM`Jd@gkh4XH85M$Z9zmG&!C2o{3 z8LBkgVAwR4&7LQ1u?$|ZhaxF%-SA|_wL^Ss<#5yeWIsei<%H<*v9;@qb@kDFgd*(5 zjZLbPX7v_3Z}rwM8Iwd5V$Akz6M z^h;pqyyGl_f|LpH&v}y(up{!>$9tp>cE5LUxQht)x!J=Q1D+}OvnPydW=+{5g{xOz zVX{@H;{&ERq>5FvpC&PX{{lc0y>+r3ToEDN5U;4KAh!J~Ft=cvq`4p@woqBZb7#?u zwN&lZ_98Hs@RiuG!X&hG=G@Z5HjT}f#j+k2ZTuvmDpA@4I{V5ef{AGB%xAq$9TR8% z9(I@>w+r?PI~Ya+Km-5nZNLV53RRW zo?;QR-atQUAtCWP5f}D2EH}jclK*^iA+P*$r!;rE^x^zOn)MO8MYpVavVgy4yFuca zZnj|!KzMtT>X7eoV2|MXUNo|LH~!aG=>>kfrpQ*wT6nc8I5r~3$olGtlhOhrz;D#q z3Q&~Sbi`~qQ@GRiDcHqST$*NBr<0Nh$@RkV*HNMDkvOO`M8fz zRerP-BT};1z$7+jAv*68%=DtT1c^2b#cv?l-KZz|=<2Ros)MR%D6u8ET$h{bxJ0S^hp}@O1HQeC%X~+4Ier?q2?u z&X;n(PBaEC2Zo|m7me4(xx*aQwHp27LZt(Af|^hgV=jM*S%NL!Fqp_WWk$8NH?qD+JL3{yA z@b?Z+b~n4<`^<`l4KEL!>cz7#kZc*)Sm6kAj+H?r@&nVB=TggnI6V05#bQo@Zhzgf zJ@4anxiTg`5%tOEt5izu8r7!>_LkXS z*>FV}aNfa~)A57N%|>~c+RM1NaC_#Sg4j)5)#f6}!g2~hb}dKNXJ_2T9m5+KkvHA6 z{oUnz2%i8$Nov6#MRt7fq*zn!tZ z6jfHP%RXS%+RfPoQPfe$OfK6yX>if-_NF7P8Y|It`26kvbd9V*?*Zh#RFPR>A|Dm{ zZ1{apSFv8As^~E8zCZlU(w(+3L&y;^0td6b#kBJ+$NP(9FV3{C7StXisA{6!8)j^jO{@xB?S z=E1nqU2Cz=;P2;*k}0{n@(#f-nozRLXN5$X9~VHCxS{TXUau|SKw(UByZ6WK#)GOzUe_aG>fcUa_b1Dfs!ZC{KP}sOlkG$3FC|sH$c!m1tyoK0QnZ~3 zNJRshHM*$EI1#(&h>~F>TLW%&nvDEu2lX1##YzAMI?Xmv)%ug@w?z^;d^HB$aM`Y+ zE?m_^e1KQ^JUlE|i`s6sqdjz82+=4lRRtLI7pph^6QB8hSLfU~ewcytsKV`Wm)eMs z-@oN~|6aXwca`@wk!C0}hpZJ9&^q;UQn2CWjK$jb z86CN;3TKK3g0;dUSBiP~4(T9GqQcSrJ?zm_e9Tz^gt-q)`Q>!JHJ+Ct?_AJ+(mpxe(YP7K#ss6ve=sn-u&$PV1s1^##oc3z~g>=Y%eUD9Z zEQmx_(-8#s2j7s9SbQlZ{%Z($wAI9vfX-O%LQvgrImh|!)fMU(p#TS?!s&WyG?(lV&sU2V!HCY7iO<&o>z>U0ves}!Mn0@ zy&UlLmpl*{Egh|df{p_oo_&>WoX_e~e*Ka>XBcIm1sN@$5G8(Vi?gVC9Lf2+^-A!4 zB@85{m6+W~sC$A`G+G1eDGB_L$ESF{Z!p;AkMW`g>e6anEDU5O-jJVgypg~o*nDK0 z1MmrnO`Dg9HIIj|UC9h_!mIiRf+} zyTg8Q#!K=1H<7x6+R$GtAOLM(UTKW2c3N8Re_)^ed<;NfwpS?QoYc{5k+t>SoV0#DJ60q7TOhjJ^0j-%XPe<;$H?=zqM`oL+?5RF z!aWUAXFa7+a%Y<#qK(1!I&!DKL`3DLhW04_>Rx?*j2CtE^45giq-dRqmr+fjRD21# z68|rqRtNi~#V_7Ojx`1!10pV4C=@c0{1<_@_=_rpPZcrccIwGGKBZ+f6p4B_h%AJo z&XRtXClV4ffn8zAqHWxytS=_}fWFrG#bJv17W)x+9n#{kDZ64A(9Hw23$+^-h4|faiH_Wj*K<42+YzhM zyq6Es^F~r7oCx~)(!mg$tsKB}3Q2N6>?-JVqF7$1uhnitcx;os_gqYo$Gjz2;%VKl+*Xmu%9AR&_ud3o>7ve1b;Gc7BuQ1wqDEI z?q8SkNV^?#N_nF^z2_ct6Wk5mV z`y-wqMn#NTw>Q-#jC#Z)2UCjs0qY>)WMWY68!4aiB?jYFKOpjLmNT-Cvq_E zq0nPHO95H%CU&(>^ycdIesBAz!&YudAnD0-_{Ntni`Ru_u|T3Yjs+2aZ?5)Cjk4*f z)s4y*2Ekvc~Y+$W_o%natUHH&4mcXKVs#v)A!G0grmmaull#~VrE{+`hZRk3dbr@5kdk&z%= zO&iu=+)3vKokBRk+rP&{{^b;Ks0@55SIkyHkMN86QQpNSj#sojFrZRx>flgg4TZoT zqkOp&f?N^?kDZv@JSoyqV<6D>3-+vugrO~t`!7sqluwuWB$kUEGswmi1X1#5$*3o* zuSxCBG%V{MI!1%8sEq0r-1%ww@gK;$ynTnSbo8V|D;8~y8Nitw-HE^;IlnC~+;y=v z45rOg@>M5rErpz6Vp*CE4RK^a{#A#>KaMonT~e_#w|hXdjkeVT=7eiaFbK>IIm=vo zUX|xfR>G|J_&3Nn2h3JJGVwCz2$U45>?bksQm#tL%SJPMbi024jxPS=RQjUO(8uTX zpm&^r`UQ&zwaeQL{+C3LksDR;tq*5(TgA7W%~t>V-zO}K64Kwziw<)GwxlC|TpEd& z=6KV&oh*$5SSSg56P^kvopWMZ_$Qv55$m=c0Ll#_O0jqbh*_I8dm?^k)SluIzmmgN zesp`AKM^>Y2lNiCRZ8m&;4<}C%7t7;cPg!0VyH@fH6Vn=q+?yy%Z0kja)h7EJt?}{ zut1U8u7^gSR{+(#QnydaS(aU?sU?lj>WP^zMZQjROr#-6g+cXFmhxJ;X6q2T({myXXJfAl3*4rzv{tsT#kEi zcy#w#VExT9n>3`_#Ib^p<_Yp5@et?yx2=#C8VW(w zo%L6$PUH~e0nRR~?H<<#ynA<|STE*yMZWfwU))*WYK%=>xg6Few!-BAe9wHHJT4pe zb7;+5G=?Mrrvt9&qhW{{uFxN} z{7(}PE?MC zLetl+VC!2*q~{F%?lHfp!h^pjg`}IY4LnJ|XnoN;{Rktu46@vV5>nUKPt&`C)V?On zM09Tzk!;?@xBe59Eqdgef64j-?3Qx{=}Zbr~y=LFH34LT`5t|(%glx~Ov z913Fb<<#eW8g`cemwzBkY=Y)@0~~GKQ_;G}-YvRYB8f6iHpTbcK1llgnVIY~O_sd+UKjUG9 z=)7-m3kwxUwHJ-K_98!A91S50-~%w#j`BeToz`^D44sP(Ko0sn0VDLs@i{HH6-eJ; zrm(V%#O0JZOihu`${-z&H4Xii5fy@~@vd8)2Ealhq})T^9JIdV!Xxl|t3>Uzu zZ*#%}-*lN3So-44;^@S7TiJET<-gydE9G??;!O~@#9pGUQ)4Z(WazbW#;^U}>5Ffn zSMS)6uzt9>Jdq!KoX;lXErf zZZoalo`1yEA|US7K0U#@Y!nmJ;xpD$OEyVNGPWjUd@AI6Q>vHWVK}Z>IYN*fKZSK) z^uyyXZj1K#hqnD~%*_yk*MPWgSqG@b>o!@508qfz`7XXr8I)=1`eiyTcH^B>)m(H& zXKZ2TWT=PmP^Ei(3zOGaBYG`;D0$XpTd86y2sOyX8q_Ux zqyA{PCwE*Q%!2b-=6Ffsf4%ONvF`7U8>ydojE035IPUgt$uEqjqm9OXEX_?@q2i^P7lr`ezvF#kQ>adV> zy#m_<^qKWb;@2+CU5kq2D+vwqVRPS2`e2?a<@(WWTI{UB`|j#9TJ}JYsvD41yc%t2 zNKuL!Fl+To%ux?j@2F3%V>+9ipBSVq^t^Krx(TK!ObNOrfP^_AE+KcsP4@S}oWbC_ zKVovZ)que0N@JvorvWB2zjPt{;i`84=bdX6lz~a4s+1`ryqbYZxCIo~tKhO-` z0+x^AK}`{Y6auEceKM7i-MPDNbs&T%BNT;3c>$M+DOl-Y|F(uWD~AwsIW4pS`mX4V z^s{7%O5yvaFF(hv1H#BVX_`wVV;m2k2`%Lx z6%?ohi8Bn0BV;Y)Acx>QKu7NXEonlsl;r@mNlb5>w37QrBqh8rDeOt_$w;Axj`EY6 z`u{DtA_eyREU7n7{l8RU6iA@J>Ye8D#S#Ud3?4_+A%k)0jpC+%ug&{G29g7}1xIA- z$!N4wOCt}AaR7AjeDCy9Q}n#A`A0Ywga+}|*qXHz?`2o@OplNAOxd|kZHslmtU28)8jwfO7>d>8@6ios{a6a7(qv_MFX`RVDSzupEsCw!avzZ}nl00r1d#J;8Vt);4u+rB+PQ0QR@iyX`- zkf7}nAO^YEj-kvc|0{DFyAr>Wj9xtm@(5?u-NB>-t_dF;9ts&~X?apsrYmUM#eM(M zS(;ebVK18g<5Zps3CO*(h)B?CCm0ukqZzlf^V9530|-qTuz;;HUH3*X!IV34g#+Za z-MGW3k9&lep!%xxU!C091x#NWYycn!H2^RMHib1R8rdju6n`Ge;PtVc-bOChsjpT2 z#{KCAR4jXKAXX6gua-_26|jqH)ZY+E?pboXZIo(LD&;en&$%I;GSBi5^7f@gt7t%1 z4>&m6)oy;{lWT=UDJNfEbp6L?BM)(qE<}E$E+l^BE@Xa`E|xD6m(OPa#Zj7VJ1Ql{ Rmns)TMnX}%M${5 literal 0 HcmV?d00001 diff --git a/version/108/frontend/img/trust_fre.png b/version/108/frontend/img/trust_fre.png new file mode 100644 index 0000000000000000000000000000000000000000..ed1a2f6f03c56f6748aac8af0352de7fc0afd3a4 GIT binary patch literal 17319 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 Mu#8ZJpsxS_1AJc%;s5{u literal 0 HcmV?d00001 diff --git a/version/108/frontend/img/trust_ger.png b/version/108/frontend/img/trust_ger.png new file mode 100644 index 0000000000000000000000000000000000000000..e42821b06709e8f4371c66cfb5759d00df0e1afd GIT binary patch literal 15352 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 (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, "[" . 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; + + if ($payable) { + $this->updateMollieCustomer($_SESSION['Kunde']); + } + + 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 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(); + } + + if (!$oMolliePayment->getCheckoutUrl() && ($oMolliePayment->isAuthorized() || $oMolliePayment->isPaid())) { + header('Location: ' . $oMolliePayment->redirectUrl); + echo "redirect to order ..."; + } else { + header('Location: ' . $oMolliePayment->getCheckoutUrl()); + echo "redirect to payment ..."; + } + unset($_SESSION['oMolliePayment']); + + 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(); + } + } + + /** + * @param $oKunde Kunde + */ + public function updateMollieCustomer($oKunde) + { + if (!$oKunde->kKunde || (int)$oKunde->nRegistriert <= 0) { + return; + } + try { + $customerId = Shop::DB()->select('xplugin_ws_mollie_kunde', 'kKunde', (int)$oKunde->kKunde); + $api = JTLMollie::API(); + /** @var Customer $customer */ + $customer = new stdClass(); + if ($customerId && isset($customerId->customerId)) { + try { + $customer = $api->customers->get($customerId->customerId); + } catch (Exception $e) { + Helper::logExc($e); + } + } + + $customer->name = utf8_encode(trim($oKunde->cVorname . ' ' . $oKunde->cNachname)); + $customer->email = utf8_encode($oKunde->cMail); + $customer->locale = self::getLocale($_SESSION['cISOSprache'], $_SESSION['Kunde']->cLand); + $customer->metadata = [ + 'kKunde' => (int)$oKunde->kKunde, + 'kKundengruppe' => (int)$oKunde->kKundengruppe, + 'cKundenNr' => utf8_encode($oKunde->cKundenNr), + ]; + + if ($customer instanceof Customer) { + $customer->update(); + } else { + if ($customer = $api->customers->create((array)$customer)) { + if (self::getMollieCustomerId($oKunde->kKunde) === false) { + Shop::DB()->insert('xplugin_ws_mollie_kunde', (object)[ + 'kKunde' => (int)$oKunde->kKunde, + 'customerId' => $customer->id, + ]); + } else { + Shop::DB()->update('xplugin_ws_mollie_kunde', 'kKunde', (int)$oKunde->kKunde, (object)[ + 'customerId' => $customer->id, + ]); + } + + } + } + + } catch (Exception $e) { + Helper::logExc($e); + } + } + + /** + * @return MollieApiClient + * @throws ApiException + * @throws IncompatiblePlatform + */ + public static function API() + { + Helper::init(); + if (self::$_mollie === null) { + self::$_mollie = new MollieApiClient(new Client([ + RequestOptions::VERIFY => CaBundle::getBundledCaBundlePath(), + RequestOptions::TIMEOUT => 60, + ])); + 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; + } + + 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 static function getMollieCustomerId($kKunde) + { + if ($row = Shop::DB()->select('xplugin_ws_mollie_kunde', 'kKunde', (int)$kKunde)) { + return $row->customerId; + } + return false; + } + + /** + * @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(), + 'metadata' => ['originalOrderNumber' => utf8_encode($order->cBestellNr)], + '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 (($customerId = self::getMollieCustomerId((int)$_SESSION['Kunde']->kKunde)) !== false) { + if (!array_key_exists('payment', $data)) { + $data['payment'] = new stdClass(); + } + $data['payment']->customerId = $customerId; + } + + 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 (array_key_exists('Kunde', $_SESSION)) { + if (isset($_SESSION['Kunde']->dGeburtstag) && trim($_SESSION['Kunde']->dGeburtstag) !== '0000-00-00' && preg_match('/^\d{4}-\d{2}-\d{2}$/', trim($_SESSION['Kunde']->dGeburtstag))) { + + $data['consumerDateOfBirth'] = trim($_SESSION['Kunde']->dGeburtstag); + } + if (isset($_SESSION['Kunde']->cAdressZusatz) && trim($_SESSION['Kunde']->cAdressZusatz) !== '') { + $data['billingAddress']->streetAdditional = utf8_encode(trim($_SESSION['Kunde']->cAdressZusatz)); + } + } + + 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; + + if (array_key_exists('Lieferadresse', $_SESSION) && isset($_SESSION['Lieferadresse']->cAdressZusatz) && trim($_SESSION['Lieferadresse']->cAdressZusatz) !== '') { + $data['shippingAddress']->streetAdditional = utf8_encode(trim($_SESSION['Lieferadresse']->cAdressZusatz)); + } + } + + /** @var WarenkorbPos $oPosition */ + foreach ($order->Positionen as $oPosition) { + + $line = new stdClass(); + $line->name = utf8_encode($oPosition->cName); + + $_netto = round($oPosition->fPreis, 2); + $_vatRate = (float)$oPosition->fMwSt / 100; + $_amount = (float)$oPosition->nAnzahl; + + if (Helper::getSetting("supportQ") === 'Y') { + // Rationale Stückzahlen aktiviert + if ((int)$oPosition->nPosTyp === (int)C_WARENKORBPOS_TYP_ARTIKEL && $oPosition->Artikel->cTeilbar === 'Y' + && fmod($oPosition->nAnzahl, 1) !== 0.0) { + $_netto *= $_amount; + $_amount = 1; + $line->name .= sprintf(" (%.2f %s)", (float)$oPosition->nAnzahl, $oPosition->cEinheit); + } + } + + $unitPriceNetto = round(($_currencyFactor * $_netto), 2); + $unitPrice = round($unitPriceNetto * (1 + $_vatRate), 2); + $totalAmount = round($_amount * $unitPrice, 2); + $vatAmount = round($totalAmount - ($totalAmount / (1 + $_vatRate)), 2); + + $line->quantity = (int)$_amount; + $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 = utf8_encode($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 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() + { + + if (array_key_exists('mollieDeleteToken', $_REQUEST) && (int)$_REQUEST['mollieDeleteToken'] === 1) { + unset($_SESSION['mollieCardToken']); + unset($_SESSION['mollieCardTokenTS']); + } + + /** @var Warenkorb $wk */ + $wk = $_SESSION['Warenkorb']; + if (Helper::getSetting("supportQ") !== 'Y') { + // Rationale Stückzahlen vorhanden? + 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 { + $amount = $wk->gibGesamtsummeWaren(true) * $_SESSION['Waehrung']->fFaktor; + if ($amount <= 0) { + $amount = 0.01; + } + $method = self::PossiblePaymentMethods(static::MOLLIE_METHOD, $locale, $_SESSION['Kunde']->cLand, $_SESSION['Waehrung']->cISO, $amount); + 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 if ((int)$this->duringCheckout === 0 && static::MOLLIE_METHOD === '') { + return true; + } + 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/108/paymentmethod/JTLMollieApplePay.php b/version/108/paymentmethod/JTLMollieApplePay.php new file mode 100644 index 0000000..ecf20a8 --- /dev/null +++ b/version/108/paymentmethod/JTLMollieApplePay.php @@ -0,0 +1,8 @@ + time() + && array_key_exists('mollieCardToken', $_SESSION) && trim($_SESSION['mollieCardToken']) !== '') { + return true; + } + + unset($_SESSION['mollieCardToken']); + unset($_SESSION['mollieCardTokenTS']); + + if (array_key_exists('cardToken', $aPost_arr) && trim($aPost_arr['cardToken'])) { + $_SESSION['mollieCardToken'] = trim($aPost_arr['cardToken']); + $_SESSION['mollieCardTokenTS'] = time() + 3600; + return true; + } + + Shop::Smarty()->assign('profileId', $profileId) + ->assign('errorMessage', json_encode(utf8_encode(Helper::oPlugin()->oPluginSprachvariableAssoc_arr['mcErrorMessage']))) + ->assign('locale', self::getLocale($_SESSION['cISOSprache'], $_SESSION['Kunde']->cLand)) + ->assign('testmode', strpos(trim(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/108/paymentmethod/JTLMollieDirectDebit.php b/version/108/paymentmethod/JTLMollieDirectDebit.php new file mode 100644 index 0000000..ce0441d --- /dev/null +++ b/version/108/paymentmethod/JTLMollieDirectDebit.php @@ -0,0 +1,8 @@ + + {$oMollieException->getMessage()} + +{/if} \ No newline at end of file diff --git a/version/108/paymentmethod/tpl/mollieComponents.tpl b/version/108/paymentmethod/tpl/mollieComponents.tpl new file mode 100644 index 0000000..825bc9f --- /dev/null +++ b/version/108/paymentmethod/tpl/mollieComponents.tpl @@ -0,0 +1,103 @@ +

{$mollieLang.cctitle}

+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+ + +
+ +
+ {if $trustBadge} +
+ PCI-DSS SAQ-A compliant +
+ {/if} +
+ + + + + \ No newline at end of file diff --git a/version/108/sql/107.sql b/version/108/sql/107.sql new file mode 100644 index 0000000..debecf2 --- /dev/null +++ b/version/108/sql/107.sql @@ -0,0 +1,4 @@ +CREATE TABLE `xplugin_ws_mollie_kunde` ( + `kKunde` int NOT NULL PRIMARY KEY, + `customerId` varchar(32) NOT NULL +); \ No newline at end of file