From 6444f7a919c1b540b6ff9ece63d3e843be77640a Mon Sep 17 00:00:00 2001 From: Matthias Reuter Date: Tue, 6 May 2014 01:14:52 +0200 Subject: [PATCH] 1.6 * Major Update! * Common: payment channel SEPA added * Common: update to new API PHP Wrapper * Common: better theme support * MagicMembers: released as final * WooCommerce: many bugs fixed, support for webhooks added * PayButton: completely rewritten --- CHANGES.md | 10 + FAQ.md | 12 +- README.md | 34 +- .../shopp-addons}/paymill.php | 4 +- lib/api/.gitignore | 4 + lib/api/.travis.yml | 12 + lib/api/Apiclient/Curl.php | 190 ---- lib/api/Apiclient/Interface.php | 20 - lib/api/Base.php | 146 --- lib/api/Clients.php | 14 - lib/api/Exception.php | 17 - lib/api/LICENSE | 15 + lib/api/Log.php | 67 -- lib/api/LoggingInterface.php | 14 - lib/api/Offers.php | 14 - lib/api/PaymentProcessor.php | 583 ------------ lib/api/Payments.php | 27 - lib/api/Preauthorizations.php | 27 - lib/api/README.md | 105 +++ lib/api/Refunds.php | 56 -- lib/api/Subscriptions.php | 14 - lib/api/Transactions.php | 27 - lib/api/Webhooks.php | 14 - lib/api/autoload.php | 18 + lib/api/build.xml | 54 ++ lib/api/composer.json | 19 + .../lib/Paymill/API/CommunicationAbstract.php | 20 + lib/api/lib/Paymill/API/Curl.php | 144 +++ .../Paymill/API}/paymill.crt | 0 lib/api/lib/Paymill/Models/Request/Base.php | 73 ++ lib/api/lib/Paymill/Models/Request/Client.php | 100 ++ lib/api/lib/Paymill/Models/Request/Offer.php | 178 ++++ .../lib/Paymill/Models/Request/Payment.php | 98 ++ .../Models/Request/Preauthorization.php | 154 ++++ lib/api/lib/Paymill/Models/Request/Refund.php | 94 ++ .../Paymill/Models/Request/Subscription.php | 181 ++++ .../Paymill/Models/Request/Transaction.php | 327 +++++++ .../lib/Paymill/Models/Request/Webhook.php | 141 +++ lib/api/lib/Paymill/Models/Response/Base.php | 115 +++ .../lib/Paymill/Models/Response/Client.php | 119 +++ lib/api/lib/Paymill/Models/Response/Error.php | 88 ++ lib/api/lib/Paymill/Models/Response/Offer.php | 174 ++++ .../lib/Paymill/Models/Response/Payment.php | 355 ++++++++ .../Models/Response/Preauthorization.php | 168 ++++ .../lib/Paymill/Models/Response/Refund.php | 167 ++++ .../Paymill/Models/Response/Subscription.php | 264 ++++++ .../Paymill/Models/Response/Transaction.php | 374 ++++++++ .../lib/Paymill/Models/Response/Webhook.php | 122 +++ lib/api/lib/Paymill/Request.php | 261 ++++++ .../lib/Paymill/Services/PaymillException.php | 56 ++ .../lib/Paymill/Services/ResponseHandler.php | 414 +++++++++ lib/api/tests/bootstrap.php | 21 + lib/api/tests/integration/ClientTest.php | 156 ++++ lib/api/tests/integration/OfferTest.php | 129 +++ lib/api/tests/integration/PaymentTest.php | 125 +++ .../integration/PreauthorizationTest.php | 131 +++ lib/api/tests/integration/RefundTest.php | 136 +++ .../tests/integration/SubscriptionTest.php | 149 +++ lib/api/tests/integration/TransactionTest.php | 155 ++++ lib/api/tests/integration/WebhookTest.php | 147 +++ lib/api/tests/phpunit.xml.dist | 14 + lib/api/tests/unit/Paymill/API/CurlTest.php | 128 +++ .../unit/Paymill/Models/Request/BaseTest.php | 67 ++ .../Paymill/Models/Request/ClientTest.php | 83 ++ .../unit/Paymill/Models/Request/OfferTest.php | 87 ++ .../Paymill/Models/Request/PaymentTest.php | 78 ++ .../Models/Request/PreauthorizationTest.php | 87 ++ .../Paymill/Models/Request/RefundTest.php | 75 ++ .../Models/Request/SubscriptionTest.php | 94 ++ .../Models/Request/TransactionTest.php | 117 +++ .../Paymill/Models/Request/WebhookTest.php | 87 ++ .../unit/Paymill/Models/Response/BaseTest.php | 58 ++ .../Paymill/Models/Response/ClientTest.php | 58 ++ .../Paymill/Models/Response/ErrorTest.php | 50 + .../Paymill/Models/Response/OfferTest.php | 68 ++ .../Paymill/Models/Response/PaymentTest.php | 90 ++ .../Models/Response/PreauthorizationTest.php | 67 ++ .../Paymill/Models/Response/RefundTest.php | 67 ++ .../Models/Response/SubscriptionTest.php | 76 ++ .../Models/Response/TransactionTest.php | 91 ++ .../Paymill/Models/Response/WebhookTest.php | 65 ++ .../unit/Paymill/Services/RequestTest.php | 502 +++++++++++ .../Paymill/Services/ResponseHandlerTest.php | 684 ++++++++++++++ lib/benchmark.inc.php | 57 ++ lib/config.inc.php | 852 +++++++++--------- lib/css/paymill.css | 243 +++-- lib/css/paymill_admin.css | 70 ++ lib/errors.inc.php | 55 ++ lib/img/cc_logos.png | Bin 20955 -> 0 bytes lib/img/cc_logos_v.png | Bin 21857 -> 0 bytes lib/img/cc_logos_woocommerce.png | Bin 20955 -> 0 bytes lib/img/creditcard-icons.png | Bin 9230 -> 0 bytes lib/img/logos/cb.png | Bin 0 -> 2954 bytes lib/img/logos/elv.png | Bin 3168 -> 3212 bytes lib/img/logos/sepa.png | Bin 0 -> 3928 bytes lib/img/payment_logos.png | Bin 0 -> 19704 bytes lib/img/tooltip.png | Bin 0 -> 2344 bytes lib/img/tooltip_2.png | Bin 0 -> 2622 bytes lib/integration/client.inc.php | 71 +- lib/integration/magicmembers.inc.php | 410 ++++----- lib/integration/pay_button.inc.php | 365 +++++--- lib/integration/payment.inc.php | 49 +- lib/integration/shopplugin.inc.php | 248 +++-- lib/integration/subscriptions.inc.php | 233 +++-- lib/integration/woocommerce.inc.php | 733 +++++++++++---- lib/js/jquery.dctooltip.1.0.js | 89 ++ lib/js/jquery.hoverIntent.minified.js | 9 + lib/js/livevalidation_custom.js | 38 +- lib/js/paymill.js | 266 +++--- lib/js/paymill_admin.js | 3 +- lib/loader.inc.php | 107 +++ lib/scripts.inc.php | 75 ++ lib/setup.inc.php | 95 ++ lib/tpl/checkout_form.php | 111 ++- lib/tpl/mgm/payment/paymill/html/settings.php | 13 +- lib/tpl/pay_button.php | 155 ++-- lib/translate/paymill-de_DE.po | 771 ++++++++-------- lib/translate/paymill-en_US.po | 782 ++++++++-------- lib/translate/paymill-sr_RS.po | 709 ++++++++------- manual_de.pdf | Bin 571102 -> 319107 bytes manual_en.pdf | Bin 612637 -> 315294 bytes paymill.php | 192 ++-- readme.txt | 310 +++++++ uninstall.php | 14 + 124 files changed, 12718 insertions(+), 4053 deletions(-) rename copy_this/shopp/{gateways/Paymill => wp-content/shopp-addons}/paymill.php (55%) create mode 100644 lib/api/.gitignore create mode 100644 lib/api/.travis.yml delete mode 100644 lib/api/Apiclient/Curl.php delete mode 100644 lib/api/Apiclient/Interface.php delete mode 100644 lib/api/Base.php delete mode 100644 lib/api/Clients.php delete mode 100644 lib/api/Exception.php create mode 100644 lib/api/LICENSE delete mode 100644 lib/api/Log.php delete mode 100644 lib/api/LoggingInterface.php delete mode 100644 lib/api/Offers.php delete mode 100644 lib/api/PaymentProcessor.php delete mode 100644 lib/api/Payments.php delete mode 100644 lib/api/Preauthorizations.php create mode 100644 lib/api/README.md delete mode 100644 lib/api/Refunds.php delete mode 100644 lib/api/Subscriptions.php delete mode 100644 lib/api/Transactions.php delete mode 100644 lib/api/Webhooks.php create mode 100644 lib/api/autoload.php create mode 100644 lib/api/build.xml create mode 100644 lib/api/composer.json create mode 100644 lib/api/lib/Paymill/API/CommunicationAbstract.php create mode 100644 lib/api/lib/Paymill/API/Curl.php rename lib/api/{Apiclient => lib/Paymill/API}/paymill.crt (100%) create mode 100644 lib/api/lib/Paymill/Models/Request/Base.php create mode 100644 lib/api/lib/Paymill/Models/Request/Client.php create mode 100644 lib/api/lib/Paymill/Models/Request/Offer.php create mode 100644 lib/api/lib/Paymill/Models/Request/Payment.php create mode 100644 lib/api/lib/Paymill/Models/Request/Preauthorization.php create mode 100644 lib/api/lib/Paymill/Models/Request/Refund.php create mode 100644 lib/api/lib/Paymill/Models/Request/Subscription.php create mode 100644 lib/api/lib/Paymill/Models/Request/Transaction.php create mode 100644 lib/api/lib/Paymill/Models/Request/Webhook.php create mode 100644 lib/api/lib/Paymill/Models/Response/Base.php create mode 100644 lib/api/lib/Paymill/Models/Response/Client.php create mode 100644 lib/api/lib/Paymill/Models/Response/Error.php create mode 100644 lib/api/lib/Paymill/Models/Response/Offer.php create mode 100644 lib/api/lib/Paymill/Models/Response/Payment.php create mode 100644 lib/api/lib/Paymill/Models/Response/Preauthorization.php create mode 100644 lib/api/lib/Paymill/Models/Response/Refund.php create mode 100644 lib/api/lib/Paymill/Models/Response/Subscription.php create mode 100644 lib/api/lib/Paymill/Models/Response/Transaction.php create mode 100644 lib/api/lib/Paymill/Models/Response/Webhook.php create mode 100644 lib/api/lib/Paymill/Request.php create mode 100644 lib/api/lib/Paymill/Services/PaymillException.php create mode 100644 lib/api/lib/Paymill/Services/ResponseHandler.php create mode 100644 lib/api/tests/bootstrap.php create mode 100644 lib/api/tests/integration/ClientTest.php create mode 100644 lib/api/tests/integration/OfferTest.php create mode 100644 lib/api/tests/integration/PaymentTest.php create mode 100644 lib/api/tests/integration/PreauthorizationTest.php create mode 100644 lib/api/tests/integration/RefundTest.php create mode 100644 lib/api/tests/integration/SubscriptionTest.php create mode 100644 lib/api/tests/integration/TransactionTest.php create mode 100644 lib/api/tests/integration/WebhookTest.php create mode 100644 lib/api/tests/phpunit.xml.dist create mode 100644 lib/api/tests/unit/Paymill/API/CurlTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Request/BaseTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Request/ClientTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Request/OfferTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Request/PaymentTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Request/PreauthorizationTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Request/RefundTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Request/SubscriptionTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Request/TransactionTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Request/WebhookTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Response/BaseTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Response/ClientTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Response/ErrorTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Response/OfferTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Response/PaymentTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Response/PreauthorizationTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Response/RefundTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Response/SubscriptionTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Response/TransactionTest.php create mode 100644 lib/api/tests/unit/Paymill/Models/Response/WebhookTest.php create mode 100644 lib/api/tests/unit/Paymill/Services/RequestTest.php create mode 100644 lib/api/tests/unit/Paymill/Services/ResponseHandlerTest.php create mode 100644 lib/benchmark.inc.php create mode 100644 lib/css/paymill_admin.css create mode 100644 lib/errors.inc.php delete mode 100644 lib/img/cc_logos.png delete mode 100644 lib/img/cc_logos_v.png delete mode 100644 lib/img/cc_logos_woocommerce.png delete mode 100644 lib/img/creditcard-icons.png create mode 100644 lib/img/logos/cb.png create mode 100644 lib/img/logos/sepa.png create mode 100644 lib/img/payment_logos.png create mode 100644 lib/img/tooltip.png create mode 100644 lib/img/tooltip_2.png create mode 100644 lib/js/jquery.dctooltip.1.0.js create mode 100644 lib/js/jquery.hoverIntent.minified.js create mode 100644 lib/loader.inc.php create mode 100644 lib/scripts.inc.php create mode 100644 lib/setup.inc.php create mode 100644 readme.txt create mode 100644 uninstall.php diff --git a/CHANGES.md b/CHANGES.md index 67def80..b1070e9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,16 @@ Changelog ========= +1.6 +----- +* Major Update! +* Common: payment channel SEPA added +* Common: update to new API PHP Wrapper +* Common: better theme support +* MagicMembers: released as final +* WooCommerce: many bugs fixed, support for webhooks added +* PayButton: completely rewritten + 1.5.2 ----- * Common: "Fatal error: Call to a member function payment_complete() on a non-object" fixed diff --git a/FAQ.md b/FAQ.md index ebb5a7c..e2e76c4 100644 --- a/FAQ.md +++ b/FAQ.md @@ -8,6 +8,9 @@ Is this plugin for free? This plugin is for free and licensed to GPL. It's open source following the GPL policy. += How can I revert all changes or reset the plugin? = +Paymill for WordPress brings an uninstall routine which allows completely removing all data generated by the plugin from WordPress. This is a great feature for a plugin reset, too which helps if there were upgrade or go live issues. You just have to go to your WordPress Admin Dashboard -> Plugins Overview and deactivate Paymill for WordPress. After that, a "delete" Link will appear which allows completely removing Paymill for WordPress. + Does this plugin calls to another server? ----------------------------------------- Yes. As Pamill is a payment service provider, it is absolutely required to call home to make sure that the payments are valid. @@ -15,7 +18,7 @@ We are talking about three different reasons for calling home: 1. PAYMILL Javascript Bridge makes sure that payment data is correct and creates a payment token delivered to your server after checkout. This avoids delivering payment data to your server, what is -in most cases- absolutely prohibited by all common credit card providers. 2. PAYMILL PHP Bridge finishes the order and delivers the generated token to the PAYMILL server. -3. For security purposes we will implement a feature which delivers WordPress version number and PAYMILL Plugin version number upon payment process. This will give us the ability to warn paymill merchants who are using a very outdated WordPress version or about known security holes in specific version when still using them. +3. For security purposes there is a feature which delivers WordPress version number and PAYMILL Plugin version number upon payment process. This will give us the ability to warn paymill merchants who are using a very outdated WordPress version or about known security holes in specific version when still using them. Are there any fees for payments? -------------------------------- @@ -45,6 +48,11 @@ What is ELV and why it's supported? ELV is a German banking service and stands for "Elektronisches Lastschriftverfahren". This is a very convenience payment solution for German users, as credit cards are not very common in Germany compared to e.g. USA. +What is SEPA and why it's supported? +----------------------------------- +SEPA is a European banking service and stands for "Single Euro Payments Area". +This is a very convenience payment solution for European users, as credit cards are not very common in Europe compared to e.g. USA. + Can I use shortcodes to display the Pay Button? ----------------------------------------------- Yes, here's an example shortcode with all currently available parameters: '[paymill_pb title="test title" products_list="1,2"]' @@ -52,4 +60,4 @@ Yes, here's an example shortcode with all currently available parameters: '[paym Are there actions/filters/hooks in the Pay Button? -------------------------------------------------- Yes, all of them have 1 parameter as array with several vars. -You may use var_dump to get their content and structure. +You may use var_dump to get their content and structure. \ No newline at end of file diff --git a/README.md b/README.md index 10bf64a..f332df8 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ PAYMILL for WordPress * Contributors: Matthias Reuter * Donate link: * Tags: paymill, creditcard, elv, payment, woocommerce, paybutton, ecommerce, debitcard, subscriptions -* Requires at least: 3.5 -* Tested up to: 3.8 -* Stable tag: 1.5.2 +* Requires at least: 3.9 +* Tested up to: 3.9 +* Stable tag: 1.6 * License: [GPLv3 or later](http://www.gnu.org/licenses/gpl-3.0.html) With PAYMILL you are able to provide credit card based payments for your customers. @@ -18,13 +18,10 @@ Plugin Description This plugin currently allows: -* Payment Gateway for WooCommerce, including subscription support +* Payment Gateway for WooCommerce - incl. subscription support * Payment Gateway for ShopPlugin -* Pay Button. including subscription support - -Features in Development: - -* Payment Gateway for Magic Members (70% done) - beta for subscriptions included +* Payment Gateway for Magic Members - incl. subscription support +* Pay Button - incl. subscription support Service Description =================== @@ -38,7 +35,7 @@ Top-notch customer support. Subscriptions supported and Mobile SDKs for iOS and Android available. Accept payments in up to 100 currencies. All major card brands like MasterCard, VISA, American Express, Diner's Club, Maestro etc. supported. -Available in 39 countries across Europe so far. +Available in 40 countries across Europe so far. Installation ============ @@ -49,8 +46,10 @@ There is a manual included in English and German as PDF, but in short: 2. Activate the plugin through the 'Plugins' menu in WordPress 3. Register a free account on https://www.paymill.com/ 4. Insert TEST Api Keys for testing purposes in plugin settings -5. If you are happy how the plugin works, enable your live account on https://www.paymill.com/ - this could take a couple of days. -6. After your paymill account has been activated for LIVE mode, switch your account to live and replace your TEST API Keys with your LIVE API Keys in plugin settings. +5. Integrate the widget somewhere in your wordpress. Without one of the supported shops, the easiest method is the pay button, available in the admin interface at _Design » Widgets_, or [as a shortcode](FAQ.md) for individual pages. +6. Play around in test mode. +7. If you are happy how the plugin works, enable your live account on https://www.paymill.com/ - this could take a couple of days. +8. After your paymill account has been activated for LIVE mode, switch your account to live and replace your TEST API Keys with your LIVE API Keys in plugin settings. actions ------- @@ -62,20 +61,13 @@ actions filters ------- * paymill_paybutton_order_desc +* paymill_paybutton_client_desc * paymill_paybutton_email_text Customizing the Pay Button -------------------------- -The Pay Button is made for customizing and you should make intensive use of CSS to cutomize it. Examples: - -```css -/* hide country selection */ -.paymill_shipping { display: none; } - -/* hide company name */ -.paymill_address div:nth-child(2) { display: none; } -``` +The Pay Button is made for customizing and you should make intensive use of CSS to cutomize it. Additionally, you may want to replace the default order form with your own. Create a custom theme file on `THEME_DIR/paymill/pay_button.php`, diff --git a/copy_this/shopp/gateways/Paymill/paymill.php b/copy_this/shopp/wp-content/shopp-addons/paymill.php similarity index 55% rename from copy_this/shopp/gateways/Paymill/paymill.php rename to copy_this/shopp/wp-content/shopp-addons/paymill.php index 0d8e260..8c06fbf 100644 --- a/copy_this/shopp/gateways/Paymill/paymill.php +++ b/copy_this/shopp/wp-content/shopp-addons/paymill.php @@ -11,6 +11,8 @@ * @subpackage PaymillShopp **/ -require_once(PAYMILL_DIR.'lib/integration/shopplugin.inc.php'); +if(defined('PAYMILL_DIR') && file_exists(PAYMILL_DIR.'lib/integration/shopplugin.inc.php')){ + require_once(PAYMILL_DIR.'lib/integration/shopplugin.inc.php'); +} ?> \ No newline at end of file diff --git a/lib/api/.gitignore b/lib/api/.gitignore new file mode 100644 index 0000000..01d0f7b --- /dev/null +++ b/lib/api/.gitignore @@ -0,0 +1,4 @@ +/.idea/ +/build/ +/vendor +phpunit.xml \ No newline at end of file diff --git a/lib/api/.travis.yml b/lib/api/.travis.yml new file mode 100644 index 0000000..9d3f1c5 --- /dev/null +++ b/lib/api/.travis.yml @@ -0,0 +1,12 @@ +--- +php: +- "5.5" +- "5.4" +- "5.3" +script: ant test +env: + global: + secure: "StaRWxgVW55YXMetUfL91rGDHJCBC0e3Nv9qYx5jg2aa9H0BIYhJ8vYtDXbKFEeGXWRnlLdPVclzRiIMdYQTZaBrjHxWVzE9N5stIyIq/Ik5hvkRs3h78ICSsDJB7SAun+aYv4vV/2kR44B0YiOhWZehV3IhAWO1Csd4HDjiQHY=" +language: php +before_install: composer self-update +install: composer install diff --git a/lib/api/Apiclient/Curl.php b/lib/api/Apiclient/Curl.php deleted file mode 100644 index b61f4b4..0000000 --- a/lib/api/Apiclient/Curl.php +++ /dev/null @@ -1,190 +0,0 @@ -_apiKey = $apiKey; - $this->_apiUrl = $apiEndpoint; - /** - * Proxy support. The proxy can be SOCKS5 or HTTP. - * Also the connection could be tunneled through. - */ - if (!empty($extracURL)) { - $this->_extraOptions = $extracURL; - } - } - - /** - * Perform API and handle exceptions - * - * @param $action - * @param array $params - * @param string $method - * @return mixed - * @throws Services_Paymill_Exception - * @throws Exception - */ - public function request($action, $params = array(), $method = 'POST') - { - if (!is_array($params)) - $params = array(); - - try { - $this->_responseArray = $this->_requestApi($action, $params, $method); - $httpStatusCode = $this->_responseArray['header']['status']; - if ($httpStatusCode != 200) { - $errorMessage = 'Client returned HTTP status code ' . $httpStatusCode; - if (isset($this->_responseArray['body']['error'])) { - $errorMessage = $this->_responseArray['body']['error']; - } - $responseCode = ''; - if (isset($this->_responseArray['body']['response_code'])) { - $responseCode = $this->_responseArray['body']['response_code']; - } - if ($responseCode === '' && isset($this->_responseArray['body']['data']['response_code'])) { - $responseCode = $this->_responseArray['body']['data']['response_code']; - } - - return array('data' => array( - 'error' => $errorMessage, - 'response_code' => $responseCode, - 'http_status_code' => $httpStatusCode - )); - } - - return $this->_responseArray['body']; - } catch (Exception $e) { - return array("data" => array("error" => $e->getMessage())); - } - } - - /** - * Perform HTTP request to REST endpoint - * - * @param string $action - * @param array $params - * @param string $method - * @return array - */ - protected function _requestApi($action = '', $params = array(), $method = 'POST') - { - $curlOpts = array( - CURLOPT_URL => $this->_apiUrl . $action, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_CUSTOMREQUEST => $method, - CURLOPT_USERAGENT => self::USER_AGENT, - CURLOPT_SSL_VERIFYPEER => true, - CURLOPT_CAINFO => realpath(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'paymill.crt', - ); - - // Add extra options to cURL if defined. - if (!empty($this->_extraOptions)) { - $curlOpts += $this->_extraOptions; - } - - if (Services_Paymill_Apiclient_Interface::HTTP_GET === $method) { - if (0 !== count($params)) { - $curlOpts[CURLOPT_URL] .= false === strpos($curlOpts[CURLOPT_URL], '?') ? '?' : '&'; - $curlOpts[CURLOPT_URL] .= http_build_query($params, null, '&'); - } - } else { - $curlOpts[CURLOPT_POSTFIELDS] = http_build_query($params, null, '&'); - } - - if ($this->_apiKey) { - $curlOpts[CURLOPT_USERPWD] = $this->_apiKey . ':'; - } - - $curl = curl_init(); - curl_setopt_array($curl, $curlOpts); - $responseBody = curl_exec($curl); - self::$lastRawCurlOptions = $curlOpts; - self::$lastRawResponse = $responseBody; - $responseInfo = curl_getinfo($curl); - if ($responseBody === false) { - $responseBody = array('error' => curl_error($curl)); - } - curl_close($curl); - - if ('application/json' === $responseInfo['content_type']) { - $responseBody = json_decode($responseBody, true); - } - - return array( - 'header' => array( - 'status' => $responseInfo['http_code'], - 'reason' => null, - ), - 'body' => $responseBody - ); - } - - /** - * Returns the response of the request as an array. - * @return mixed Response - * @todo Create Unit Test - */ - public function getResponse() - { - return $this->_responseArray; - } - -} \ No newline at end of file diff --git a/lib/api/Apiclient/Interface.php b/lib/api/Apiclient/Interface.php deleted file mode 100644 index 224b825..0000000 --- a/lib/api/Apiclient/Interface.php +++ /dev/null @@ -1,20 +0,0 @@ -_httpClient = new Services_Paymill_Apiclient_Curl($apiKey, $apiEndpoint, $extraData); - } - - /** - * General REST GET verb - * - * @param array $filters e.g. count,offest - * @param string $identifier resource id - * - * @return array of resource items - */ - public function get($filters = array(), $identifier = '') - { - $response = $this->_httpClient->request( - $this->_serviceResource . $identifier, - $filters, - Services_Paymill_Apiclient_Interface::HTTP_GET - ); - - return $response['data']; - } - - /** - * General REST GET verb - * returns one item or null - * - * @param string $identifier resource id - * - * @return array resource item | null - */ - public function getOne($identifier = null) - { - if (!$identifier) { - return null; - } - - $filters = array("count" => 1, 'offset' => 0); - - return $this->get($filters, $identifier); - } - - /** - * General REST DELETE verb - * Delete or inactivate/cancel resource item - * - * @param string $clientId - * - * @return array item deleted - */ - public function delete($clientId = null) - { - $response = $this->_httpClient->request( - $this->_serviceResource . $clientId, - array(), - Services_Paymill_Apiclient_Interface::HTTP_DELETE - ); - - return $response['data']; - } - - /** - * General REST POST verb - * create resource item - * - * @param array $itemData - * - * @return array created item - */ - public function create($itemData = array()) - { - $response = $this->_httpClient->request( - $this->_serviceResource, - $itemData, - Services_Paymill_Apiclient_Interface::HTTP_POST - ); - - return $response['data']; - } - - /** - * General REST PUT verb - * Update resource item - * - * @param array $itemData - * - * @return array item updated or null - */ - public function update(array $itemData = array()) - { - if (!isset($itemData['id']) ) { - return null; - } - - $itemId = $itemData['id']; - unset ($itemData['id']); - - $response = $this->_httpClient->request( - $this->_serviceResource . $itemId, - $itemData, - Services_Paymill_Apiclient_Interface::HTTP_PUT - ); - - return $response['data']; - } - - /** - * Returns the response of the last request as an array - * @return mixed Response - * @todo Add Unit test - */ - public function getResponse(){ - return $this->_httpClient->getResponse(); - } -} \ No newline at end of file diff --git a/lib/api/Clients.php b/lib/api/Clients.php deleted file mode 100644 index e8e861a..0000000 --- a/lib/api/Clients.php +++ /dev/null @@ -1,14 +0,0 @@ -$name = $value; - } - - /** - * Magic getter to avoid the access of undefined vars - * @param type $name - * @return null - */ - public function __get($name) - { - if (isset($this->$name)) { - return $this->$name; - } - - return null; - } - - /** - * Fill the model with the given json data - * @param string $json - */ - public function fill($json) - { - $data = json_decode($json, true); - - foreach ($data as $name => $value) { - $this->$name = $value; - } - } - - /** - * To array - * - * @return array - */ - public function toArray() - { - $data = array(); - foreach ($this as $key => $value) { - $data[$key] = $value; - } - - return $data; - } - - /** - * To json - * - * @return string - */ - public function toJson() - { - return json_encode($this->toArray()); - } -} \ No newline at end of file diff --git a/lib/api/LoggingInterface.php b/lib/api/LoggingInterface.php deleted file mode 100644 index 696f71e..0000000 --- a/lib/api/LoggingInterface.php +++ /dev/null @@ -1,14 +0,0 @@ -$privateKey Paymill-PrivateKey - * @param String $apiUrl Paymill-Api Url - * @param String $libBase Path to the lib Base (Can be null, Default Path will be used) - * @param array $params(
- * token, generated Token
- * amount, Basketamount
- * currency, Transaction currency
- * name, Customer name
- * email, Customer emailaddress
- * description, Description for transactions
- * )

(If not set here, the use of setters is required for the class to work)

- * @param object $loggingClassInstance Instance of Object implementing the Services_Paymill_PaymentProcessorInterface. If not set, there will be no logging. - */ - public function __construct($privateKey = null, $apiUrl = null, $libBase = null, $params = null, Services_Paymill_LoggingInterface $loggingClassInstance = null) - { - $this->setPrivateKey($privateKey); - $this->setApiUrl($apiUrl); - $this->setLibBase($libBase); - $this->_preAuthAmount = $params['preauthamount']; - $this->_token = $params['token']; - $this->_amount = $params['amount']; - $this->_currency = $params['currency']; - $this->_name = $params['name']; - $this->_email = $params['email']; - $this->_description = $params['description']; - $this->setLogger($loggingClassInstance); - } - - /** - * Creates a Paymill-Client with the given Data - * - * @param array $params - * @return boolean - */ - private function _createClient() - { - if (isset($this->_clientId)) { - $this->_log("Client using: " . $this->_clientId); - } else { - $client = $this->_clientsObject->create( - array( - 'email' => $this->_email, - 'description' => $this->_description - ) - ); - - $this->_validateResult($client, 'Client'); - - $this->_clientId = $client['id']; - } - return $this->_clientId; - } - - /** - * Creates a Paymill-Payment with the given Data - * - * @param array $params - * @return boolean - */ - private function _createPayment() - { - if (isset($this->_paymentId)) { - $this->_log("Payment using: " . $this->_paymentId); - } else { - $payment = $this->_paymentsObject->create( - array( - 'token' => $this->_token, - 'client' => $this->_clientId - ) - ); - $this->_validateResult($payment, 'Payment'); - - $this->_paymentId = $payment['id']; - } - return true; - } - - /** - * Creates a Paymill-Transaction with the given Data - * - * @param array $params - * @return boolean - */ - private function _createTransaction() - { - $parameter = array( - 'amount' => $this->_amount, - 'currency' => $this->_currency, - 'description' => $this->_description, - 'source' => $this->_source - ); - $this->_preauthId != null ? $parameter['preauthorization'] = $this->_preauthId : $parameter['payment'] = $this->_paymentId ; - $transaction = $this->_transactionsObject->create($parameter); - $this->_validateResult($transaction, 'Transaction'); - - $this->_transactionId = $transaction['id']; - return true; - } - - /** - * Creates a Paymill-Transaction with the given Data - * - * @param array $params - * @return boolean - */ - private function _createPreauthorization() - { - $preAuth = $this->_preauthObject->create( - array( - 'amount' => $this->_preAuthAmount, - 'currency' => $this->_currency, - 'description' => $this->_description, - 'payment' => $this->_paymentId, - 'client' => $this->_clientId, - ) - ); - $this->_validateResult($preAuth, 'Preauthorization'); - $this->_preauthId = $preAuth['preauthorization']['id']; - return true; - } - - /** - * Load the PhpWrapper-Classes and creates an instance for each class. - */ - private function _initiatePhpWrapperClasses() - { - require_once $this->_libBase . 'Transactions.php'; - require_once $this->_libBase . 'Preauthorizations.php'; - require_once $this->_libBase . 'Clients.php'; - require_once $this->_libBase . 'Payments.php'; - - $this->_transactionsObject = new Services_Paymill_Transactions($this->_privateKey, $this->_apiUrl); - $this->_preauthObject = new Services_Paymill_Preauthorizations($this->_privateKey, $this->_apiUrl); - $this->_clientsObject = new Services_Paymill_Clients($this->_privateKey, $this->_apiUrl); - $this->_paymentsObject = new Services_Paymill_Payments($this->_privateKey, $this->_apiUrl); - } - - /** - * Calls the log() function of the logger object if the object has been set. - * - * @param string $message - * @param string $debugInfo - */ - private function _log($message, $debugInfo = null) - { - if (isset($this->_logger)) { - $this->_logger->log($message, $debugInfo); - } - } - - /** - * Validates the array passed as an argument to be processPayment() compliant - * @param mixed $parameter - * @return boolean - */ - private function _validateParameter() - { - if ($this->_preAuthAmount == null) { - $this->_preAuthAmount = $this->_amount; - } - - $validation = true; - $parameter = array( - "token" => $this->_token, - "amount" => $this->_amount, - "currency" => $this->_currency, - "name" => $this->_name, - "email" => $this->_email, - "description" => $this->_description); - - $arrayMask = array( - "token" => 'string', - "amount" => 'integer', - "currency" => 'string', - "name" => 'string', - "email" => 'string', - "description" => 'string'); - - foreach ($arrayMask as $mask => $type) { - if (is_null($parameter[$mask])) { - $validation = false; - $this->_logger->paramName = $mask; - $this->_log("The Parameter $mask is missing.", var_export($parameter, true)); - } else { - switch ($type) { - case 'string': - if (!is_string($parameter[$mask])) { - $this->_logger->paramName = $mask; - $this->_log("The Parameter $mask is not a string.", var_export($parameter, true)); - $validation = false; - } - break; - case 'integer': - if (!is_integer($parameter[$mask])) { - $this->_logger->paramName = $mask; - $this->_log("The Parameter $mask is not an integer.", var_export($parameter, true)); - $validation = false; - } - break; - } - } - - if (!$validation) { - break; - } - } - return $validation; - } - - /** - * Validates the created Paymill-Objects - * - * @param array $transaction - * @param string $type - * @return boolean - */ - private function _validateResult($transaction, $type) - { - $this->_lastResponse = $transaction; - if (isset($transaction['data']['response_code']) && $transaction['data']['response_code'] !== 20000) { - $this->_logger->paramName = 'response_error'; - $this->_log("An Error occured: " . $transaction['data']['response_code'], var_export($transaction, true)); - throw new Exception("Invalid Result Exception: Invalid ResponseCode"); - } - - if (!isset($transaction['id']) && !isset($transaction['data']['id'])) { - $this->_logger->paramName = $type . '_error'; - $this->_log("No $type created.", var_export($transaction, true)); - throw new Exception("Invalid Result Exception: Invalid Id"); - } else { - $this->_logger->paramName = $type . '_success'; - $this->_log("$type created.", isset($transaction['id']) ? $transaction['id'] : $transaction['data']['id']); - } - - // check result - if ($type == 'Transaction') { - if (is_array($transaction) && array_key_exists('status', $transaction)) { - if ($transaction['status'] == "closed") { - // transaction was successfully issued - return true; - } elseif ($transaction['status'] == "open") { - // transaction was issued but status is open for any reason - $this->_logger->paramName = 'transaction_success'; - $this->_log("Status is open.", var_export($transaction, true)); - throw new Exception("Invalid Result Exception: Invalid Orderstate"); - } else { - // another error occured - $this->_logger->paramName = 'unknown_error'; - $this->_log("Unknown error." . var_export($transaction, true)); - throw new Exception("Invalid Result Exception: Unknown Error"); - } - } else { - // another error occured - $this->_logger->paramName = $type . '_error'; - $this->_log("$type could not be issued.", var_export($transaction, true)); - throw new Exception("Invalid Result Exception: $type could not be issued."); - } - } else { - return true; - } - } - - private function _processPreAuthCapture($captureNow) - { - $this->_createPreauthorization(); - if ($captureNow) { - $this->_createTransaction(); - } - return true; - } - - /** - * Executes the Payment Process - * - * @return boolean - */ - final public function processPayment($captureNow = true) - { - $this->_initiatePhpWrapperClasses(); - if (!$this->_validateParameter()) { - return false; - } - - $this->_logger->paramName = 'process_payment_data'; - $this->_log('Process payment with following data', print_r($this->toArray(), true)); - - try { - - $this->_createClient(); - $this->_logger->paramName = 'client_api_response'; - $this->_log('Client API Response', print_r($this->_clientsObject->getResponse(), true)); - $this->_createPayment(); - $this->_logger->paramName = 'payment_api_response'; - $this->_log('Payment API Response', print_r($this->_paymentsObject->getResponse(), true)); - - //creates a transaction if there is no difference between the amount - if ($this->_preAuthAmount === $this->_amount && $captureNow) { - $this->_createTransaction(); - $this->_logger->paramName = 'transaction_api_response'; - $this->_log('Transaction API Response', print_r($this->getLastResponse(), true)); - } else { - $this->_processPreAuthCapture($captureNow); - $this->_logger->paramName = 'pre_auth_api_response'; - $this->_log('Pre-Auth API Response', print_r($this->getLastResponse(), true)); - } - - return true; - } catch (Exception $ex) { - // paymill wrapper threw an exception - $this->_logger->paramName = 'exception_response'; - $this->_log("Exception thrown from paymill wrapper.", $ex->getMessage()); - return false; - } - } - - final public function capture() - { - $this->_initiatePhpWrapperClasses(); - if (!isset($this->_amount) || !isset($this->_currency) || !isset($this->_preauthId)) { - return false; - } - return $this->_createTransaction(); - } - - /** - * Returns the objects data - * - * @return array - */ - public function toArray() - { - return array( - 'apiurl' => $this->_apiUrl, - 'libbase' => $this->_libBase, - 'privatekey' => $this->_privateKey, - 'token' => $this->_token, - 'amount' => $this->_amount, - 'preauthamount' => $this->_preAuthAmount, - 'currency' => $this->_currency, - 'description' => $this->_description, - 'email' => $this->_email, - 'name' => $this->_name, - 'source' => $this->_source - ); - } - - /* * ************************************************************************************************************** - * *********************************************** Getter ************************************************** - * *************************************************************************************************************** */ - - /** - *

Can only be called after the call of processPayment(). Otherwise null will be returned

- * Returns the ClientId - * @return String ClientId - */ - public function getClientId() - { - return $this->_clientId; - } - - /** - *

Can only be called after the call of processPayment(). Otherwise null will be returned

- * Returns the PaymentId - * @return String PaymentId - */ - public function getPaymentId() - { - return $this->_paymentId; - } - - /** - *

Can only be called after the call of processPayment(). Otherwise null will be returned

- * Returns the TransactionId - * @return String TransactionId - */ - public function getTransactionId() - { - return $this->_transactionId; - } - - /** - *

Can only be called after the call of processPayment(). Otherwise null will be returned

- * Returns the preauthId - * @return String preauthId - */ - public function getPreauthId() - { - return $this->_preauthId; - } - - /** - *

Can only be called after the call of processPayment(). Otherwise null will be returned

- * Returns the last response send by Paymill - * @return array LastResponse - */ - public function getLastResponse() - { - return $this->_lastResponse; - } - - /* * ************************************************************************************************************** - * *********************************************** Setter ************************************************** - * *************************************************************************************************************** */ - - /** - * Sets the clientId - * @param String $clientId - */ - public function setClientId($clientId = null) - { - $this->_clientId = $clientId; - } - - /** - * Sets the paymentId - * @param String $paymentId - */ - public function setPaymentId($paymentId = null) - { - $this->_paymentId = $paymentId; - } - - /** - * This method sets the token - * @param String $token - */ - public function setToken($token = null) - { - $this->_token = $token; - } - - /** - * This method sets the preAuthAmount - * @param String $preAuthAmount - */ - public function setPreAuthAmount($preAuthAmount = null) - { - $this->_preAuthAmount = $preAuthAmount; - } - - /** - * This method sets the amount - * @param String $amount - */ - public function setAmount($amount = null) - { - $this->_amount = $amount; - } - - /** - * Sets the currency - * @param String $currency - */ - public function setCurrency($currency = null) - { - $this->_currency = $currency; - } - - /** - * Sets the Customer name - * @param String $name - */ - public function setName($name = null) - { - $this->_name = $name; - } - - /** - * Sets the Customer Email Adress - * @param String $email - */ - public function setEmail($email = null) - { - $this->_email = $email; - } - - /** - * Sets the Description - * @param String $description - */ - public function setDescription($description = null) - { - $this->_description = $description; - } - - /** - * Sets the Api URL - * @param String $apiUrl - */ - public function setApiUrl($apiUrl = null) - { - $this->_apiUrl = $apiUrl; - } - - /** - * Sets the Path to the libBase - * @param String $libBase Path to the Lib base. If not set, the default path is set. - */ - public function setLibBase($libBase = null) - { - $this->_libBase = $libBase == null ? dirname(__FILE__) . DIRECTORY_SEPARATOR : $libBase; - } - - /** - * Sets up the Logger Object. - * The Logger object can be any class implementing the Services_Paymill_PaymentProcessorInterface. - * @param any $logger - */ - public function setLogger(Services_Paymill_LoggingInterface $logger = null) - { - $this->_logger = $logger; - } - - /** - * Sets the Paymill-PrivateKey - * @param string $privateKey - */ - public function setPrivateKey($privateKey = null) - { - $this->_privateKey = $privateKey; - } - - /** - * Set the request source - * (Modulversion_Shopname_Shopversion) - * @param string $source - */ - public function setSource($source) - { - $this->_source = $source; - } - - /** - * Set PreauthorizationID to be captured - * - * @param string $preauthId - */ - public function setPreauthId($preauthId) - { - $this->_preauthId = $preauthId; - } - -} \ No newline at end of file diff --git a/lib/api/Payments.php b/lib/api/Payments.php deleted file mode 100644 index 81b9a41..0000000 --- a/lib/api/Payments.php +++ /dev/null @@ -1,27 +0,0 @@ -setToken("098f6bcd4621d373cade4e832627b4f6"); + ``` +3. Use your desired function: + + ```php + $response = $request->create($payment); + $paymentId = $response->getId(); + ``` + + It recommend to wrap it into a "try/catch" to handle exceptions like this: + ```php + try{ + $response = $request->create($payment); + $paymentId = $response->getId(); + }catch(PaymillException $e){ + //Do something with the error informations below + $e->getResponseCode(); + $e->getStatusCode(); + $e->getErrorMessage(); + } + ``` + +Receiving Response +-------------- + +This section shows diffrent ways how to receive a response. +The followings examples show how to get the Id for a transaction. + +1. The default response is one of the response-models. +```php + $response = $request->create($payment); + $response->getId(); +``` + +2. getLastResponse() returns the unconverted response from the API. +```php + $request->create($payment); + $response = $request->getLastResponse(); + $response['body']['data']['id']; +``` + +3. getJSONObject returns the response as stdClass-Object. +```php + $request->create($payment); + $response = $request->getJSONObject(); + $response->data->id; +``` + +Documentation +-------------- + +For further information, please refer to our official PHP library reference: https://www.paymill.com/en-gb/documentation-3/reference/api-reference/index.html diff --git a/lib/api/Refunds.php b/lib/api/Refunds.php deleted file mode 100644 index 34c640c..0000000 --- a/lib/api/Refunds.php +++ /dev/null @@ -1,56 +0,0 @@ -_httpClient->request( - $this->_serviceResource . "$transactionId", - $params, - Services_Paymill_Apiclient_Interface::HTTP_POST - ); - return $result['data']; - } - - /** - * General REST DELETE verb - * Delete or inactivate/cancel resource item - * - * @param string $clientId - * - * @return array item deleted - */ - public function delete($identifier = null) - { - throw new Services_Paymill_Exception( __CLASS__ . " does not support " . __METHOD__, "404"); - } - - /** - * {@inheritDoc} - */ - public function update(array $itemData = array()) - { - throw new Services_Paymill_Exception( __CLASS__ . " does not support " . __METHOD__, "404" ); - } -} \ No newline at end of file diff --git a/lib/api/Subscriptions.php b/lib/api/Subscriptions.php deleted file mode 100644 index 5fb20c0..0000000 --- a/lib/api/Subscriptions.php +++ /dev/null @@ -1,14 +0,0 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/api/composer.json b/lib/api/composer.json new file mode 100644 index 0000000..705a6cd --- /dev/null +++ b/lib/api/composer.json @@ -0,0 +1,19 @@ +{ + "name": "paymill/paymill", + "type": "library", + "description": "Paymill PHPLib", + "keywords": ["payment", "provider"], + "license": "MIT", + "homepage": "https://www.paymill.de", + "authors": [ + { + "name": "Paymill GmbH", + "homepage": "https://www.paymill.de" + } + ], + "autoload": { + "psr-0": { + "Paymill": "lib/" + } + } +} diff --git a/lib/api/lib/Paymill/API/CommunicationAbstract.php b/lib/api/lib/Paymill/API/CommunicationAbstract.php new file mode 100644 index 0000000..4dc7dcf --- /dev/null +++ b/lib/api/lib/Paymill/API/CommunicationAbstract.php @@ -0,0 +1,20 @@ +_apiKey = $apiKey; + $this->_apiUrl = $apiEndpoint; + /** + * Proxy support. The proxy can be SOCKS5 or HTTP. + * Also the connection could be tunneled through. + */ + $this->_extraOptions = $extracURL; + } + + /** + * Perform HTTP request to REST endpoint + * + * @param string $action + * @param array $params + * @param string $method + * @return array + */ + public function requestApi($action = '', $params = array(), $method = 'POST') + { + $curlOpts = array( + CURLOPT_URL => $this->_apiUrl . $action, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CUSTOMREQUEST => $method, + CURLOPT_USERAGENT => 'Paymill-php/0.0.2', + CURLOPT_SSL_VERIFYPEER => true, + CURLOPT_CAINFO => realpath(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'paymill.crt', + ); + + // Add extra options to cURL if defined. + if (!empty($this->_extraOptions)) { + $curlOpts = $this->_extraOptions + $curlOpts; + } + + if ('GET' === $method) { + if (0 !== count($params)) { + $curlOpts[CURLOPT_URL] .= false === strpos($curlOpts[CURLOPT_URL], '?') ? '?' : '&'; + $curlOpts[CURLOPT_URL] .= http_build_query($params, null, '&'); + } + } else { + $curlOpts[CURLOPT_POSTFIELDS] = http_build_query($params, null, '&'); + } + + if ($this->_apiKey) { + $curlOpts[CURLOPT_USERPWD] = $this->_apiKey . ':'; + } + + $curl = curl_init(); + curl_setopt_array($curl, $curlOpts); + $responseBody = $this->_curlExec($curl); + $responseInfo = $this->_curlInfo($curl); + + if ($responseBody === false) { + $responseBody = array('error' => $this->_curlError($curl)); + } + curl_close($curl); + + if ('application/json' === $responseInfo['content_type']) { + $responseBody = json_decode($responseBody, true); + } + + return array( + 'header' => array( + 'status' => $responseInfo['http_code'], + 'reason' => null, + ), + 'body' => $responseBody + ); + } + + /** + * Wrapps the curlExec function call + * @param cURL handle success $curl + * @return mixed + */ + protected function _curlExec($curl) + { + return curl_exec($curl); + } + + /** + * Wrapps the curlInfo function call + * @param cURL handle success $curl + * @return mixed + */ + protected function _curlInfo($curl) + { + return curl_getinfo($curl); + } + + /** + * Wrapps the curlError function call + * @param cURL handle success $curl + * @return mixed + */ + protected function _curlError($curl) + { + return curl_error($curl); + } +} diff --git a/lib/api/Apiclient/paymill.crt b/lib/api/lib/Paymill/API/paymill.crt similarity index 100% rename from lib/api/Apiclient/paymill.crt rename to lib/api/lib/Paymill/API/paymill.crt diff --git a/lib/api/lib/Paymill/Models/Request/Base.php b/lib/api/lib/Paymill/Models/Request/Base.php new file mode 100644 index 0000000..5e3b16a --- /dev/null +++ b/lib/api/lib/Paymill/Models/Request/Base.php @@ -0,0 +1,73 @@ +_serviceResource; + } + + /** + * Returns this objects unique identifier + * @return string identifier + */ + public function getId() + { + return $this->_id; + } + + /** + * Sets the unique identifier of this object + * @param string $id + * @return \Paymill\Models\Request\Base + */ + public function setId($id) + { + $this->_id = $id; + return $this; + } + /** + * Returns the filterArray for getAll + * @return array + */ + public function getFilter() + { + return $this->_filter; + } + + /** + * Sets the filterArray for getAll + * @param array $filter + * @return \Paymill\Models\Request\Base + */ + public function setFilter($filter) + { + $this->_filter = $filter; + return $this; + } +} diff --git a/lib/api/lib/Paymill/Models/Request/Client.php b/lib/api/lib/Paymill/Models/Request/Client.php new file mode 100644 index 0000000..b533790 --- /dev/null +++ b/lib/api/lib/Paymill/Models/Request/Client.php @@ -0,0 +1,100 @@ +_serviceResource = 'clients/'; + } + + /** + * Returns the Mail address of this client. + * @return string + */ + public function getEmail() + { + return $this->_email; + } + + /** + * Sets the Mail address of this client. + * @param string $email + * @return \Paymill\Models\Request\Client + */ + public function setEmail($email) + { + $this->_email = $email; + return $this; + } + + /** + * Returns the additional description for this client, perhaps the identifier from your CRM system? + * @return string + */ + public function getDescription() + { + return $this->_description; + } + + /** + * Sets an additional description for this client. We recommend some sort of identifier from your CRM system + * @param string $description + * @return \Paymill\Models\Request\Client + */ + public function setDescription($description) + { + $this->_description = $description; + return $this; + } + + /** + * Returns an array of parameters customized for the argumented methodname + * @param string $method + * @return array + */ + public function parameterize($method) + { + $parameterArray = array(); + switch ($method) { + case 'create': + case 'update': + $parameterArray['email'] = $this->getEmail(); + $parameterArray['description'] = $this->getDescription(); + break; + case 'getOne': + $parameterArray['count'] = 1; + $parameterArray['offset'] = 0; + break; + case 'getAll': + $parameterArray = $this->getFilter(); + break; + case 'delete': + break; + } + + return $parameterArray; + } +} diff --git a/lib/api/lib/Paymill/Models/Request/Offer.php b/lib/api/lib/Paymill/Models/Request/Offer.php new file mode 100644 index 0000000..d4d647e --- /dev/null +++ b/lib/api/lib/Paymill/Models/Request/Offer.php @@ -0,0 +1,178 @@ +_serviceResource = 'Offers/'; + } + + /** + * Returns Your name for this offer + * @return string + */ + public function getName() + { + return $this->_name; + } + + /** + * Sets Your name for this offer + * @param string $name + * @return \Paymill\Models\Request\Offer + */ + public function setName($name) + { + $this->_name = $name; + return $this; + } + + /** + * Returns the amount as an integer + * @return integer + */ + public function getAmount() + { + return $this->_amount; + } + + /** + * Sets the amount. + * Every interval the specified amount will be charged. Only integer values are allowed (e.g. 42.00 = 4200) + * @param integer $amount + * @return \Paymill\Models\Request\Offer + */ + public function setAmount($amount) + { + $this->_amount = (int) $amount; + return $this; + } + + /** + * Returns the interval defining how often the client should be charged. + * @return string + */ + public function getInterval() + { + return $this->_interval; + } + + /** + * Sets the interval defining how often the client should be charged. + * @example Format: number DAY | WEEK | MONTH | YEAR Example: 2 DAY + * @param string $interval + * @return \Paymill\Models\Request\Offer + */ + public function setInterval($interval) + { + $this->_interval = $interval; + return $this; + } + + /** + * Returns the number of days to try + * @return integer + */ + public function getTrialPeriodDays() + { + return $this->_trialPeriodDays; + } + + /** + * Sets the number of days to try + * @param integer $trialPeriodDays + * @return \Paymill\Models\Request\Offer + */ + public function setTrialPeriodDays($trialPeriodDays) + { + $this->_trialPeriodDays = $trialPeriodDays; + return $this; + } + + /** + * Returns the currency + * @return string + */ + public function getCurrency() + { + return $this->_currency; + } + + /** + * Sets the currency + * @param string $currency + * @return \Paymill\Models\Request\Offer + */ + public function setCurrency($currency) + { + $this->_currency = $currency; + return $this; + } + + /** + * Returns an array of parameters customized for the argumented methodname + * @param string $method + * @return array + */ + public function parameterize($method) + { + $parameterArray = array(); + switch ($method) { + case 'create': + $parameterArray['amount'] = $this->getAmount(); + $parameterArray['currency'] = $this->getCurrency(); + $parameterArray['interval'] = $this->getInterval(); + $parameterArray['name'] = $this->getName(); + $parameterArray['trial_period_days'] = $this->getTrialPeriodDays(); + break; + case 'update': + $parameterArray['name'] = $this->getName(); + break; + case 'getOne': + $parameterArray['count'] = 1; + $parameterArray['offset'] = 0; + break; + case 'getAll': + $parameterArray = $this->getFilter(); + break; + case 'delete': + break; + } + + return $parameterArray; + } +} diff --git a/lib/api/lib/Paymill/Models/Request/Payment.php b/lib/api/lib/Paymill/Models/Request/Payment.php new file mode 100644 index 0000000..e419c73 --- /dev/null +++ b/lib/api/lib/Paymill/Models/Request/Payment.php @@ -0,0 +1,98 @@ +_serviceResource = 'Payments/'; + } + + /** + * Returns the identifier of a client (client-object) + * @return string The identifier of a client (client-object) + */ + public function getClient() + { + return $this->_client; + } + + /** + * Sets the identifier of a client (client-object) + * @param string $client + * @return Payment + */ + public function setClient($client) + { + $this->_client = $client; + return $this; + } + + /** + * Returns the Token + * @return String + */ + public function getToken() + { + return $this->_token; + } + + /** + * Sets the token required for payment creation + * @param string $token + * @return Payment + */ + public function setToken($token) + { + $this->_token = $token; + return $this; + } + + /** + * Returns an array of parameters customized for the argumented methodname + * @param string $method + * @return array + */ + public function parameterize($method) + { + $parameterArray = array(); + switch ($method) { + case 'create': + $parameterArray['token'] = $this->getToken(); + $parameterArray['client'] = $this->getClient(); + break; + case 'delete': + break; + case 'getOne': + $parameterArray['count'] = 1; + $parameterArray['offset'] = 0; + break; + case 'getAll': + $parameterArray = $this->getFilter(); + break; + } + + return $parameterArray; + } +} diff --git a/lib/api/lib/Paymill/Models/Request/Preauthorization.php b/lib/api/lib/Paymill/Models/Request/Preauthorization.php new file mode 100644 index 0000000..1623204 --- /dev/null +++ b/lib/api/lib/Paymill/Models/Request/Preauthorization.php @@ -0,0 +1,154 @@ +_serviceResource = 'Preauthorizations/'; + } + + /** + * Returns the amount + * @return string + */ + public function getAmount() + { + return $this->_amount; + } + + /** + * Sets the amount + * @param string $amount + * @return \Paymill\Models\Request\Preauthorization + */ + public function setAmount($amount) + { + $this->_amount = $amount; + return $this; + } + + /** + * Returns the currency + * @return string + */ + public function getCurrency() + { + return $this->_currency; + } + + /** + * Sets the currency + * @param string $currency + * @return \Paymill\Models\Request\Preauthorization + */ + public function setCurrency($currency) + { + $this->_currency = $currency; + return $this; + } + + /** + * Returns the identifier of a payment + * @return string + */ + public function getPayment() + { + return $this->_payment; + } + + /** + * Sets the identifier of a payment + * @param string $payment + * @return \Paymill\Models\Request\Preauthorization + */ + public function setPayment($payment) + { + $this->_payment = $payment; + return $this; + } + + /** + * Returns the token required for the creation of preAuths + * @return string + */ + public function getToken() + { + return $this->_token; + } + + /** + * Sets the token required for the creation of preAuths + * @param string $token + * @return \Paymill\Models\Request\Preauthorization + */ + public function setToken($token) + { + $this->_token = $token; + return $this; + } + + /** + * Returns an array of parameters customized for the argumented methodname + * @param string $method + * @return array + */ + public function parameterize($method) + { + $parameterArray = array(); + switch ($method) { + case 'create': + if (!is_null($this->getPayment())) { + $parameterArray['payment'] = $this->getPayment(); + } else { + $parameterArray['token'] = $this->getToken(); + } + $parameterArray['amount'] = $this->getAmount(); + $parameterArray['currency'] = $this->getCurrency(); + + break; + case 'getOne': + $parameterArray['count'] = 1; + $parameterArray['offset'] = 0; + break; + case 'getAll': + $parameterArray = $this->getFilter(); + break; + case 'delete': + break; + } + + return $parameterArray; + } +} diff --git a/lib/api/lib/Paymill/Models/Request/Refund.php b/lib/api/lib/Paymill/Models/Request/Refund.php new file mode 100644 index 0000000..df6957e --- /dev/null +++ b/lib/api/lib/Paymill/Models/Request/Refund.php @@ -0,0 +1,94 @@ +_serviceResource = 'Refunds/'; + } + + /** + * Returns the amount + * @return string + */ + public function getAmount() + { + return $this->_amount; + } + + /** + * Sets the amount + * @param string $amount + * @return \Paymill\Models\Request\Refund + */ + public function setAmount($amount) + { + $this->_amount = $amount; + return $this; + } + + /** + * Returns the description + * @return string + */ + public function getDescription() + { + return $this->_description; + } + + /** + * Sets the description + * @param string $description + * @return \Paymill\Models\Request\Refund + */ + public function setDescription($description) + { + $this->_description = $description; + return $this; + } + + /** + * Returns an array of parameters customized for the argumented methodname + * @param string $method + * @return array + */ + public function parameterize($method) + { + $parameterArray = array(); + switch ($method) { + case 'create': + $parameterArray['amount'] = $this->getAmount(); + $parameterArray['description'] = $this->getDescription(); + break; + case 'getOne': + $parameterArray['count'] = 1; + $parameterArray['offset'] = 0; + break; + case 'getAll': + $parameterArray = $this->getFilter(); + break; + } + + return $parameterArray; + } +} diff --git a/lib/api/lib/Paymill/Models/Request/Subscription.php b/lib/api/lib/Paymill/Models/Request/Subscription.php new file mode 100644 index 0000000..6a0fb58 --- /dev/null +++ b/lib/api/lib/Paymill/Models/Request/Subscription.php @@ -0,0 +1,181 @@ +_serviceResource = 'Subscriptions/'; + } + + /** + * Returns the identifier of the offer the subscription is based on + * @return string + */ + public function getOffer() + { + return $this->_offer; + } + + /** + * Sets the identifier of the offer the subscription is based on + * @param string $offer + * @return \Paymill\Models\Request\Subscription + */ + public function setOffer($offer) + { + $this->_offer = $offer; + return $this; + } + + /** + * Returns the flag determining whether to cancel this subscription immediately or at the end of the current period + * @return boolean + */ + public function getCancelAtPeriodEnd() + { + return $this->_cancelAtPeriodEnd; + } + + /** + * Sets a flag determining whether to cancel this subscription immediately or at the end of the current period + * @param boolean $cancelAtPeriodEnd + * @return \Paymill\Models\Request\Subscription + */ + public function setCancelAtPeriodEnd($cancelAtPeriodEnd) + { + $this->_cancelAtPeriodEnd = $cancelAtPeriodEnd; + return $this; + } + + /** + * Returns the identifier of the payment object registered with this subscription + * @return string + */ + public function getPayment() + { + return $this->_payment; + } + + /** + * Sets the identifier of the payment object registered with this subscription + * @param string $payment + * @return \Paymill\Models\Request\Subscription + */ + public function setPayment($payment) + { + $this->_payment = $payment; + return $this; + } + + /** + * Returns the id of the client associated with this subscription + * @return string + */ + public function getClient() + { + return $this->_client; + } + + /** + * Sets the id of the client associated with this subscription + * @param string $client + * @return \Paymill\Models\Request\Subscription + */ + public function setClient($client) + { + $this->_client = $client; + return $this; + } + + /** + * Returns the Unix-Timestamp for the trial period start + * @return integer + */ + public function getStartAt() + { + return $this->_startAt; + } + + /** + * Sets the Unix-Timestamp for the trial period start + * @param integer $startAt + * @return \Paymill\Models\Request\Subscription + */ + public function setStartAt($startAt) + { + $this->_startAt = $startAt; + return $this; + } + + /** + * Returns an array of parameters customized for the argumented methodname + * @param string $method + * @return array + */ + public function parameterize($method) + { + $parameterArray = array(); + switch ($method) { + case 'create': + $parameterArray['client'] = $this->getClient(); + $parameterArray['offer'] = $this->getOffer(); + $parameterArray['payment'] = $this->getPayment(); + $parameterArray['start_at'] = $this->getStartAt(); + break; + case 'update': + $parameterArray['cancel_at_period_end'] = $this->getCancelAtPeriodEnd(); + $parameterArray['offer'] = $this->getOffer(); + $parameterArray['payment'] = $this->getPayment(); + break; + case 'getOne': + $parameterArray['count'] = 1; + $parameterArray['offset'] = 0; + break; + case 'getAll': + $parameterArray = $this->getFilter(); + break; + case 'delete': + break; + } + + return $parameterArray; + } +} diff --git a/lib/api/lib/Paymill/Models/Request/Transaction.php b/lib/api/lib/Paymill/Models/Request/Transaction.php new file mode 100644 index 0000000..3afde5e --- /dev/null +++ b/lib/api/lib/Paymill/Models/Request/Transaction.php @@ -0,0 +1,327 @@ +_serviceResource = 'Transactions/'; + } + + /** + * Returns the 'real' amount + * @return string + */ + public function getAmount() + { + return $this->_amount; + } + + /** + * Sets the 'real' amount for the transaction. + * The number musst be in the smallest currency unit and will be saved as a string + * @param string $amount + * @return \Paymill\Models\Request\Transaction + */ + public function setAmount($amount) + { + $this->_amount = $amount; + return $this; + } + + /** + * Returns the transaction description + * @return string + */ + public function getDescription() + { + return $this->_description; + } + + /** + * Sets the transaction description + * @param string $description + * @return \Paymill\Models\Request\Transaction + */ + public function setDescription($description) + { + $this->_description = $description; + return $this; + } + + /** + * Returns the currency + * @return string + */ + public function getCurrency() + { + return $this->_currency; + } + + /** + * Sets the currency + * @param string $currency + * @return \Paymill\Models\Request\Transaction + */ + public function setCurrency($currency) + { + $this->_currency = $currency; + return $this; + } + + /** + * Returns the identifier of the payment associated with the transaction + * @return string + */ + public function getPayment() + { + return $this->_payment; + } + + /** + * Sets the identifier of the Payment for the transcation + * @param string $payment + * @return \Paymill\Models\Request\Transaction + */ + public function setPayment($payment) + { + $this->_payment = $payment; + return $this; + } + + /** + * Returns the identifier of the Client associated with the transaction. If no client is available null will be returned + * @return string|null + */ + public function getClient() + { + return $this->_client; + } + + /** + * Sets the identifier of the Client for the transaction + * @param string $client + * @return \Paymill\Models\Request\Transaction + */ + public function setClient($client) + { + $this->_client = $client; + return $this; + } + + /** + * Returns the identifier of the Preauthorization associated with the transaction. If no preAuth is available null will be returned + * @return string|null + */ + public function getPreauthorization() + { + return $this->_preauthorization; + } + + /** + * Sets the identifier of the Preauthorization for the transaction + * @param string $preauthorization + * @return \Paymill\Models\Request\Transaction + */ + public function setPreauthorization($preauthorization) + { + $this->_preauthorization = $preauthorization; + return $this; + } + + /** + * Returns the FeeAmount + * Fee included in the transaction amount (set by a connected app). Mandatory if feePayment is set + * @return integer + */ + public function getFeeAmount() + { + return $this->_feeAmount; + } + + /** + * Sets the Fee included in the transaction amount (set by a connected app). + * @param integer $feeAmount + * @return \Paymill\Models\Request\Transaction + */ + public function setFeeAmount($feeAmount) + { + $this->_feeAmount = $feeAmount; + return $this; + } + + /** + * Returns the identifier of the payment from which the fee will be charged (creditcard-object or directdebit-object). + * @return string + */ + public function getFeePayment() + { + return $this->_feePayment; + } + + /** + * Sets the identifier of the payment from which the fee will be charged (creditcard-object or directdebit-object). + * @param string $feePayment + * @return \Paymill\Models\Request\Transaction + */ + public function setFeePayment($feePayment) + { + $this->_feePayment = $feePayment; + return $this; + } + + /** + * Set the currency which should be used for collecting the given fee + * @param string $feeCurrency (e.g. EUR, USD ...) + * @return \Paymill\Models\Response\Transaction + */ + public function setFeeCurrency($feeCurrency) + { + $this->_feeCurrency = $feeCurrency; + return $this; + + } + + /** + * returns the set fee currency which is used for the fee collection + * @return string + */ + public function getFeeCurrency() + { + return $this->_feeCurrency; + } + + + + /** + * Returns the token generated through our JavaScript-Bridge. + * When this parameter is used, none of the following should be used: payment, preauthorization. + * @return string + */ + public function getToken() + { + return $this->_token; + } + + /** + * Sets the token generated through our JavaScript-Bridge. + * When this parameter is used, none of the following should be used: payment, preauthorization. + * @param string $token + * @return \Paymill\Models\Request\Transaction + */ + public function setToken($token) + { + $this->_token = $token; + return $this; + } + + /** + * Returns an array of parameters customized for the argumented methodname + * @param string $method + * @return array + */ + public function parameterize($method) + { + $parameterArray = array(); + switch ($method) { + case 'create': + if (!is_null($this->getPreauthorization())) { + $parameterArray['preauthorization'] = $this->getPreauthorization(); + } elseif (!is_null($this->getPayment())) { + $parameterArray['payment'] = $this->getPayment(); + } else { + $parameterArray['token'] = $this->getToken(); + } + $parameterArray['amount'] = $this->getAmount(); + $parameterArray['currency'] = $this->getCurrency(); + $parameterArray['description'] = $this->getDescription(); + $parameterArray['client'] = $this->getClient(); + if (!is_null($this->getFeeAmount())) { + $parameterArray['fee_amount'] = $this->getFeeAmount(); + } + if (!is_null($this->getFeePayment())) { + $parameterArray['fee_payment'] = $this->getFeePayment(); + } + if (!is_null($this->getFeeCurrency())) { + $parameterArray['fee_currency'] = $this->getFeeCurrency(); + } + break; + case 'update': + $parameterArray['description'] = $this->getDescription(); + break; + case 'getAll': + $parameterArray = $this->getFilter(); + break; + case 'getOne': + $parameterArray['count'] = 1; + $parameterArray['offset'] = 0; + break; + case 'delete': + break; + } + + return $parameterArray; + } +} diff --git a/lib/api/lib/Paymill/Models/Request/Webhook.php b/lib/api/lib/Paymill/Models/Request/Webhook.php new file mode 100644 index 0000000..1943371 --- /dev/null +++ b/lib/api/lib/Paymill/Models/Request/Webhook.php @@ -0,0 +1,141 @@ +_serviceResource = 'Webhooks/'; + } + + /** + * Returns the webhook url + * @return string + */ + public function getUrl() + { + return $this->_url; + } + + /** + * Sets the webhook url + * @param string $url + * @return \Paymill\Models\Request\Webhook + */ + public function setUrl($url) + { + $this->_url = $url; + return $this; + } + + /** + * Returns the event types as an array + * @return array + */ + public function getEventTypes() + { + return $this->_eventTypes; + } + + /** + * Sets the event types for the webhook. + * There are a number of events you can react to. Each webhook can be configured to catch any kind of event + * individually, so you can create different webhooks for different events. Each Webhook needs to be attached + * to at least one event. For example the event subscription.succeeded is triggered every time a successful + * transaction has been made in our system that is based on a subscription. Shortly after that has been triggered, + * we will call every webhook you defined for this event and send detailed information to it. + * @tutorial https://paymill.com/de-de/dokumentation/referenz/api-referenz/#document-webhooks + * @param array $eventTypes + * @return \Paymill\Models\Request\Webhook + */ + public function setEventTypes($eventTypes) + { + $this->_eventTypes = $eventTypes; + return $this; + } + + /** + * Returns the email registered for this webhook + * @return string||null + */ + public function getEmail() + { + return $this->_email; + } + + /** + * Sets the email for the webhook. + * @param string $email Instead of setting the url parameter you can set the email parameter to create a webhook, + * where we send mails to in case of an event. + * @return \Paymill\Models\Request\Webhook + */ + public function setEmail($email) + { + $this->_email = $email; + return $this; + } + + /** + * Returns an array of parameters customized for the argumented methodname + * @param string $method + * @return array + */ + public function parameterize($method) + { + $parameterArray = array(); + switch ($method) { + case 'create': + if(!is_null($this->getUrl())){ + $parameterArray['url'] = $this->getUrl(); + }else{ + $parameterArray['email'] = $this->getEmail(); + } + $parameterArray['event_types'] = $this->getEventTypes(); + break; + case 'update': + if(!is_null($this->getUrl())){ + $parameterArray['url'] = $this->getUrl(); + }else{ + $parameterArray['email'] = $this->getEmail(); + } + $parameterArray['event_types'] = $this->getEventTypes(); + break; + case 'delete': + break; + case 'getOne': + $parameterArray['count'] = 1; + $parameterArray['offset'] = 0; + break; + case 'getAll': + $parameterArray = $this->getFilter(); + break; + } + + return $parameterArray; + } +} diff --git a/lib/api/lib/Paymill/Models/Response/Base.php b/lib/api/lib/Paymill/Models/Response/Base.php new file mode 100644 index 0000000..3cd5e4f --- /dev/null +++ b/lib/api/lib/Paymill/Models/Response/Base.php @@ -0,0 +1,115 @@ +_id; + } + + /** + * Sets the Unique identifier + * @param string $id + * @return \Paymill\Models\Response\Base + */ + public function setId($id) + { + $this->_id = $id; + return $this; + } + + /** + * Returns the Unix timestamp of the creation + * @return integer + */ + public function getCreatedAt() + { + return $this->_createdAt; + } + + /** + * Sets the Unix timestamp of the creation + * @param integer $createdAt + * @return \Paymill\Models\Response\Base + */ + public function setCreatedAt($createdAt) + { + $this->_createdAt = $createdAt; + return $this; + } + + /** + * Returns the Unix timestamp of the last update + * @return integer + */ + public function getUpdatedAt() + { + return $this->_updatedAt; + } + + /** + * Sets the Unix timestamp of the last update + * @param integer $updatedAt + * @return \Paymill\Models\Response\Base + */ + public function setUpdatedAt($updatedAt) + { + $this->_updatedAt = $updatedAt; + return $this; + } + + /** + * Returns the identifier of the object which created this object instance + * @return string|null + */ + public function getAppId() + { + return $this->_appId; + } + + /** + * Sets the identifier of the object which created this object instance + * @param string|null $appId + * @return \Paymill\Models\Response\Base + */ + public function setAppId($appId) + { + $this->_appId = $appId; + return $this; + } + +} diff --git a/lib/api/lib/Paymill/Models/Response/Client.php b/lib/api/lib/Paymill/Models/Response/Client.php new file mode 100644 index 0000000..a21eb2f --- /dev/null +++ b/lib/api/lib/Paymill/Models/Response/Client.php @@ -0,0 +1,119 @@ +_email; + } + + /** + * Sets the Mail address of this client. + * @param string $email + * @return \Paymill\Models\Response\Client + */ + public function setEmail($email) + { + $this->_email = $email; + return $this; + } + + /** + * Returns the additional description for this client, perhaps the identifier from your CRM system? + * @return string + */ + public function getDescription() + { + return $this->_description; + } + + /** + * Sets an additional description for this client. We recommend some sort of identifier from your CRM system + * @param string $description + * @return \Paymill\Models\Response\Client + */ + public function setDescription($description) + { + $this->_description = $description; + return $this; + } + + /** + * Returns a list of payment objects associated with this client + * @return \Paymill\Models\Response\Payment + */ + public function getPayment() + { + return $this->_payment; + } + + /** + * Sets the payment list stored in the client model + * @param \Paymill\Models\Response\Payment $payment + * @return \Paymill\Models\Response\Client + */ + public function setPayment($payment) + { + $this->_payment = $payment; + return $this; + } + + /** + * Returns a list of subscription objects associated with this client + * @return array + */ + public function getSubscription() + { + return $this->_subscription; + } + + /** + * Sets the subscription list stored in the client model + * @param array $subscription + * @return \Paymill\Models\Response\Client + */ + public function setSubscription($subscription) + { + $this->_subscription = $subscription; + return $this; + } + +} diff --git a/lib/api/lib/Paymill/Models/Response/Error.php b/lib/api/lib/Paymill/Models/Response/Error.php new file mode 100644 index 0000000..879cf20 --- /dev/null +++ b/lib/api/lib/Paymill/Models/Response/Error.php @@ -0,0 +1,88 @@ +_errorMessage; + } + + /** + * Sets the error message stored in this model + * @param string $errorMessage + * @return \Paymill\Models\Response\Error + */ + public function setErrorMessage($errorMessage) + { + $this->_errorMessage = $errorMessage; + return $this; + } + + /** + * Returns the response code + * @return int + */ + public function getResponseCode() + { + return $this->_responseCode; + } + + /** + * Sets the response code + * @param int $responseCode + * @return \Paymill\Models\Response\Error + */ + public function setResponseCode($responseCode) + { + $this->_responseCode = $responseCode; + return $this; + } + + /** + * Returns the status code + * @return int + */ + public function getHttpStatusCode() + { + return $this->_httpStatusCode; + } + + /** + * Sets the status code + * @param int $httpStatusCode + * @return \Paymill\Models\Response\Error + */ + public function setHttpStatusCode($httpStatusCode) + { + $this->_httpStatusCode = $httpStatusCode; + return $this; + } + +} diff --git a/lib/api/lib/Paymill/Models/Response/Offer.php b/lib/api/lib/Paymill/Models/Response/Offer.php new file mode 100644 index 0000000..bc7a0ed --- /dev/null +++ b/lib/api/lib/Paymill/Models/Response/Offer.php @@ -0,0 +1,174 @@ + [integer or string], inactive => [integer or string]) + */ + private $_subscriptionCount = array(); + + /** + * Returns Your name for this offer + * @return string + */ + public function getName() + { + return $this->_name; + } + + /** + * Sets Your name for this offer + * @param string $name + * @return \Paymill\Models\Response\Offer + */ + public function setName($name) + { + $this->_name = $name; + return $this; + } + + /** + * Returns the amount as an integer + * @return integer + */ + public function getAmount() + { + return $this->_amount; + } + + /** + * Sets the amount. + * Every interval the specified amount will be charged. Only integer values are allowed (e.g. 42.00 = 4200) + * @param integer $amount + * @return \Paymill\Models\Response\Offer + */ + public function setAmount($amount) + { + $this->_amount = (int)$amount; + return $this; + } + + /** + * Returns the interval defining how often the client should be charged. + * @return string + */ + public function getInterval() + { + return $this->_interval; + } + + /** + * Sets the interval defining how often the client should be charged. + * @example Format: number DAY | WEEK | MONTH | YEAR Example: 2 DAY + * @param string $interval + * @return \Paymill\Models\Response\Offer + */ + public function setInterval($interval) + { + $this->_interval = $interval; + return $this; + } + + /** + * Returns the number of days to try + * @return integer + */ + public function getTrialPeriodDays() + { + return $this->_trialPeriodDays; + } + + /** + * Sets the number of days to try + * @param integer $trialPeriodDays + * @return \Paymill\Models\Response\Offer + */ + public function setTrialPeriodDays($trialPeriodDays) + { + $this->_trialPeriodDays = $trialPeriodDays; + return $this; + } + + /** + * Returns the subscriptionCount array + * @return array + */ + public function getSubscriptionCount() + { + return $this->_subscriptionCount; + } + + /** + * Sets the subscriptionCount array + * @param string|integer $active + * @param string|integer $inactive + * @return \Paymill\Models\Response\Offer + */ + public function setSubscriptionCount($active, $inactive) + { + $this->_subscriptionCount['active'] = $active; + $this->_subscriptionCount['inactive'] = $inactive; + return $this; + } + + /** + * Returns the currency + * @return string + */ + public function getCurrency() + { + return $this->_currency; + } + + /** + * Sets the currency + * @param string $currency + * @return Offer + */ + public function setCurrency($currency) + { + $this->_currency = $currency; + return $this; + } + +} \ No newline at end of file diff --git a/lib/api/lib/Paymill/Models/Response/Payment.php b/lib/api/lib/Paymill/Models/Response/Payment.php new file mode 100644 index 0000000..e848eb2 --- /dev/null +++ b/lib/api/lib/Paymill/Models/Response/Payment.php @@ -0,0 +1,355 @@ +_type; + } + + /** + * Sets the type of the Payment (f. ex. creditcard) + * @param string $type + * @return \Paymill\Models\Response\Payment + */ + public function setType($type) + { + $this->_type = $type; + return $this; + } + + /** + * Returns the identifier of a client (client-object) + * @return string The identifier of a client (client-object) + */ + public function getClient() + { + return $this->_client; + } + + /** + * Sets the identifier of a client (client-object) + * @param string $client + * @return \Paymill\Models\Response\Payment + */ + public function setClient($client) + { + $this->_client = $client; + return $this; + } + + /** + * Returns the card type + * @return string + */ + public function getCardType() + { + return $this->_cardType; + } + + /** + * Sets the card type + * @param string $cardType + * @return \Paymill\Models\Response\Payment + */ + public function setCardType($cardType) + { + $this->_cardType = $cardType; + return $this; + } + + /** + * Returns the country + * @return string + */ + public function getCountry() + { + return $this->_country; + } + + /** + * Sets the country + * @param string $country + * @return \Paymill\Models\Response\Payment + */ + public function setCountry($country) + { + $this->_country = $country; + return $this; + } + + /** + * Returns the expiry month + * @return string + */ + public function getExpireMonth() + { + return $this->_expireMonth; + } + + /** + * Sets the expiry month + * @param string $expireMonth + * @return \Paymill\Models\Response\Payment + */ + public function setExpireMonth($expireMonth) + { + $this->_expireMonth = $expireMonth; + return $this; + } + + /** + * Returns the expiry year + * @return string + */ + public function getExpireYear() + { + return $this->_expireYear; + } + + /** + * Sets the expiry year + * @param string $expireYear + * @return \Paymill\Models\Response\Payment + */ + public function setExpireYear($expireYear) + { + $this->_expireYear = $expireYear; + return $this; + } + + /** + * Returns the card holder name + * @return string + */ + public function getCardHolder() + { + return $this->_cardHolder; + } + + /** + * Sets the card holder name + * @param string $cardHolder + * @return \Paymill\Models\Response\Payment + */ + public function setCardHolder($cardHolder) + { + $this->_cardHolder = $cardHolder; + return $this; + } + + /** + * Returns the last four digests of the number (account-/cardnumber) + * @return string + */ + public function getLastFour() + { + return $this->_lastFour; + } + + /** + * Sets the last four digests of the number (account-/cardnumber) + * @param string $lastFour + * @return \Paymill\Models\Response\Payment + */ + public function setLastFour($lastFour) + { + $this->_lastFour = $lastFour; + return $this; + } + + /** + * Returns The used Bank Code + * @return string + */ + public function getCode() + { + return $this->_code; + } + + /** + * Sets The used Bank Code + * @param string $code + * @return \Paymill\Models\Response\Payment + */ + public function setCode($code) + { + $this->_code = $code; + return $this; + } + + /** + * Returns the used account number, for security reasons the number is masked + * @return string + */ + public function getAccount() + { + return $this->_account; + } + + /** + * Sets the used account number, for security reasons the number is masked + * @param string $account + * @return \Paymill\Models\Response\Payment + */ + public function setAccount($account) + { + $this->_account = $account; + return $this; + } + + /** + * Returns the Name of the account holder + * @return string + */ + public function getHolder() + { + return $this->_holder; + } + + /** + * Sets the Name of the account holder + * @param string $holder + * @return \Paymill\Models\Response\Payment + */ + public function setHolder($holder) + { + $this->_holder = $holder; + return $this; + } + + /** + * Returns used IBAN + * + * @return string + */ + public function getIban() + { + return $this->_iban; + } + + /** + * Sets the IBAN + * + * @param string $iban + * @return \Paymill\Models\Response\Payment + */ + public function setIban($iban) + { + $this->_iban = $iban; + return $this; + } + + /** + * Returns used BIC + * + * @return string + */ + public function getBic() + { + return $this->_bic; + } + + /** + * Sets the BIC + * + * @param string $bic + * @return \Paymill\Models\Response\Payment + */ + public function setBic($bic) + { + $this->_bic = $bic; + return $this; + } + +} \ No newline at end of file diff --git a/lib/api/lib/Paymill/Models/Response/Preauthorization.php b/lib/api/lib/Paymill/Models/Response/Preauthorization.php new file mode 100644 index 0000000..286ce36 --- /dev/null +++ b/lib/api/lib/Paymill/Models/Response/Preauthorization.php @@ -0,0 +1,168 @@ +_amount; + } + + /** + * Sets the amount + * @param string $amount + * @return \Paymill\Models\Response\Preauthorization + */ + public function setAmount($amount) + { + $this->_amount = $amount; + return $this; + } + + + /** + * @var string + */ + private $_currency; + + /** + * Returns the currency + * @return string + */ + public function getCurrency() + { + return $this->_currency; + } + + /** + * Sets the currency + * @param string $currency + * @return \Paymill\Models\Response\Preauthorization + */ + public function setCurrency($currency) + { + $this->_currency = $currency; + return $this; + } + + /** + * Indicates the current status (open, pending, closed, failed, deleted, preauth) + * @var string + */ + private $_status; + + /** + * Returns the status + * @return string + */ + public function getStatus() + { + return $this->_status; + } + + /** + * Sets the status + * @param string $status + * @return \Paymill\Models\Response\Preauthorization + */ + public function setStatus($status) + { + $this->_status = $status; + return $this; + } + + /** + * Whether this preauthorization was issued while being in live mode or not + * @var boolean + */ + private $_livemode; + + /** + * Returns the livemode flag of the preAuth object + * @return boolean + */ + public function getLivemode() + { + return $this->_livemode; + } + + /** + * Sets the livemode flag of the preAuth object + * @param boolean $livemode + * @return \Paymill\Models\Response\Preauthorization + */ + public function setLivemode($livemode) + { + $this->_livemode = $livemode; + return $this; + } + + /** + * Payment Response Model + * @var Payment + */ + private $_payment; + + /** + * Returns the identifier of a payment + * @return Payment + */ + public function getPayment() + { + return $this->_payment; + } + + /** + * Sets the identifier of a payment + * @param Payment $payment + * @return \Paymill\Models\Response\Preauthorization + */ + public function setPayment($payment) + { + $this->_payment = $payment; + return $this; + } + + /** + * Client Response Model + * @var Client + */ + private $_client; + + /** + * Returns the identifier of a client + * @return Client + */ + public function getClient() + { + return $this->_client; + } + + /** + * Sets the identifier of a client + * @param Client $client + * @return \Paymill\Models\Response\Preauthorization + */ + public function setClient($client) + { + $this->_client = $client; + return $this; + } + +} \ No newline at end of file diff --git a/lib/api/lib/Paymill/Models/Response/Refund.php b/lib/api/lib/Paymill/Models/Response/Refund.php new file mode 100644 index 0000000..31fcb0c --- /dev/null +++ b/lib/api/lib/Paymill/Models/Response/Refund.php @@ -0,0 +1,167 @@ +_transaction; + } + + /** + * Sets the transaction model + * @param \Paymill\Models\Response\Transaction $transaction + * @return \Paymill\Models\Response\Refund + */ + public function setTransaction($transaction) + { + $this->_transaction = $transaction; + return $this; + } + + /** + * Amount in the smallest possible unit per currency (for euro, we’re calculating the amount in cents). + * @var integer + */ + private $_amount; + + /** + * Returns the amount + * @return integer + */ + public function getAmount() + { + return $this->_amount; + } + + /** + * Sets the amount + * @param integer $amount + * @return \Paymill\Models\Response\Refund + */ + public function setAmount($amount) + { + $this->_amount = $amount; + return $this; + } + + /** + * Indicates the current status of this refund. (open, pending, refunded) + * @var string + */ + private $_status; + + /** + * Returns the Status of the refund + * @return string + */ + public function getStatus() + { + return $this->_status; + } + + /** + * Sets the Status of the refund + * @param string $status + * @return \Paymill\Models\Response\Refund + */ + public function setStatus($status) + { + $this->_status = $status; + return $this; + } + + /** + * The description given for this refund. + * @var string + */ + private $_description; + + /** + * Returns the description of this refund + * @return string + */ + public function getDescription() + { + return $this->_description; + } + + /** + * Sets the description of this refund + * @param string $description + * @return \Paymill\Models\Response\Refund + */ + public function setDescription($description) + { + $this->_description = $description; + return $this; + } + + /** + * Whether this refund happend in test- or in livemode. + * @var boolean + */ + private $_livemode; + + /** + * Returns the Livemode flag of this refund + * @return boolean + */ + public function getLivemode() + { + return $this->_livemode; + } + + /** + * Sets the Livemode flag of this refund + * @param boolean $livemode + * @return \Paymill\Models\Response\Refund + */ + public function setLivemode($livemode) + { + $this->_livemode = $livemode; + return $this; + } + + /** + * @var integer + */ + private $_responseCode; + + /** + * Returns the response code + * @return integer + */ + public function getResponseCode() + { + return $this->_responseCode; + } + + /** + * Sets the response code + * @param integer $responseCode + * @return \Paymill\Models\Response\Refund + */ + public function setResponseCode($responseCode) + { + $this->_responseCode = $responseCode; + return $this; + } + +} \ No newline at end of file diff --git a/lib/api/lib/Paymill/Models/Response/Subscription.php b/lib/api/lib/Paymill/Models/Response/Subscription.php new file mode 100644 index 0000000..5e09de4 --- /dev/null +++ b/lib/api/lib/Paymill/Models/Response/Subscription.php @@ -0,0 +1,264 @@ +_offer; + } + + /** + * Sets the model of the offer the subscription is based on + * @param \Paymill\Models\Response\Offer $offer + * @return \Paymill\Models\Response\Subscription + */ + public function setOffer($offer) + { + $this->_offer = $offer; + return $this; + } + + /** + * Returns the flag determining whether this subscription was issued while being in live mode or not. + * @return boolean + */ + public function getLivemode() + { + return $this->_livemode; + } + + /** + * Sets the flag determining whether this subscription was issued while being in live mode or not. + * @param string $livemode + * @return \Paymill\Models\Response\Subscription + */ + public function setLivemode($livemode) + { + $this->_livemode = $livemode; + return $this; + } + + /** + * Returns the flag determining whether to cancel this subscription immediately or at the end of the current period + * @return boolean + */ + public function getCancelAtPeriodEnd() + { + return $this->_cancelAtPeriodEnd; + } + + /** + * Sets a flag determining whether to cancel this subscription immediately or at the end of the current period + * @param boolean $cancelAtPeriodEnd + * @return \Paymill\Models\Response\Subscription + */ + public function setCancelAtPeriodEnd($cancelAtPeriodEnd) + { + $this->_cancelAtPeriodEnd = $cancelAtPeriodEnd; + return $this; + } + + /** + * Returns the Unix-Timestamp for the trial period start + * @return integer + */ + public function getTrialStart() + { + return $this->_trialStart; + } + + /** + * Sets the Unix-Timestamp for the trial period start + * @param integer $trialStart + * @return \Paymill\Models\Response\Subscription + */ + public function setTrialStart($trialStart) + { + $this->_trialStart = $trialStart; + return $this; + } + + /** + * Returns the Unix-Timestamp for the trial period end. + * @return integer + */ + public function getTrialEnd() + { + return $this->_trialEnd; + } + + /** + * Sets the Unix-Timestamp for the trial period end. + * @param integer $trialEnd + * @return \Paymill\Models\Response\Subscription + */ + public function setTrialEnd($trialEnd) + { + $this->_trialEnd = $trialEnd; + return $this; + } + + /** + * Returns the Unix-Timestamp for the next charge. + * @return integer + */ + public function getNextCaptureAt() + { + return $this->_nextCaptureAt; + } + + /** + * Sets the Unix-Timestamp for the next charge. + * @param integer $nextCaptureAt + * @return \Paymill\Models\Response\Subscription + */ + public function setNextCaptureAt($nextCaptureAt) + { + $this->_nextCaptureAt = $nextCaptureAt; + return $this; + } + + /** + * Returns the Unix-Timestamp for the cancel date. + * @return integer + */ + public function getCanceledAt() + { + return $this->_canceledAt; + } + + /** + * Sets the Unix-Timestamp for the cancel date. + * @param integer $canceledAt + * @return \Paymill\Models\Response\Subscription + */ + public function setCanceledAt($canceledAt) + { + $this->_canceledAt = $canceledAt; + return $this; + } + + /** + * Returns the payment object registered with this subscription + * @return \Paymill\Models\Response\Payment + */ + public function getPayment() + { + return $this->_payment; + } + + /** + * Sets the payment object registered with this subscription + * @param \Paymill\Models\Response\Payment $payment + * @return \Paymill\Models\Response\Subscription + */ + public function setPayment($payment) + { + $this->_payment = $payment; + return $this; + } + + /** + * Returns the client associated with this subscription + * @return \Paymill\Models\Response\Client + */ + public function getClient() + { + return $this->_client; + } + + /** + * Sets the client associated with this subscription + * @param \Paymill\Models\Response\Client $client + * @return \Paymill\Models\Response\Subscription + */ + public function setClient($client) + { + $this->_client = $client; + return $this; + } + + /** + * Returns the Unix-Timestamp for the trial period start + * @return integer + */ + public function getStartAt() + { + return $this->_startAt; + } + + /** + * Sets the Unix-Timestamp for the trial period start + * @param integer $startAt + * @return \Paymill\Models\Response\Subscription + */ + public function setStartAt($startAt) + { + $this->_startAt = $startAt; + return $this; + } + +} \ No newline at end of file diff --git a/lib/api/lib/Paymill/Models/Response/Transaction.php b/lib/api/lib/Paymill/Models/Response/Transaction.php new file mode 100644 index 0000000..eab7848 --- /dev/null +++ b/lib/api/lib/Paymill/Models/Response/Transaction.php @@ -0,0 +1,374 @@ +_amount; + } + + /** + * Sets the 'real' amount for the transaction. + * The number musst be in the smallest currency unit and will be saved as a string + * @param string $amount + * @return \Paymill\Models\Response\Transaction + */ + public function setAmount($amount) + { + $this->_amount = $amount; + return $this; + } + + /** + * origin amount + * @var integer + */ + private $_originAmount; + + /** + * Returns the origin amount for the transaction. + * @return integer + */ + public function getOriginAmount() + { + return $this->_originAmount; + } + + /** + * Sets the origin amount for the transaction. + * The number musst be in the smallest currency unit and will be saved as a string + * @param integer $originAmount + * @return \Paymill\Models\Response\Transaction + */ + public function setOriginAmount($originAmount) + { + $this->_originAmount = $originAmount; + return $this; + } + + /** + * Possible status values (open, closed, failed, preauth, pending, refunded, partially_refunded, chargeback) + * @var string + */ + private $_status; + + /** + * Returns the transaction status + * @return string + */ + public function getStatus() + { + return $this->_status; + } + + /** + * Sets the transaction status + * @param string $status + * @return \Paymill\Models\Response\Transaction + */ + public function setStatus($status) + { + $this->_status = $status; + return $this; + } + + /** + * @var string + */ + private $_description; + + /** + * Returns the transaction description + * @return string + */ + public function getDescription() + { + return $this->_description; + } + + /** + * Sets the transaction description + * @param string $description + * @return \Paymill\Models\Response\Transaction + */ + public function setDescription($description) + { + $this->_description = $description; + return $this; + } + + /** + * @var boolean + */ + private $_livemode; + + /** + * Returns the livemode flag of the transaction + * @return boolean + */ + public function getLivemode() + { + return $this->_livemode; + } + + /** + * Sets the livemode flag of the transaction + * @param boolean $livemode + * @return \Paymill\Models\Response\Transaction + */ + public function setLivemode($livemode) + { + $this->_livemode = $livemode; + return $this; + } + + /** + * @var array + */ + private $_refunds = null; + + /** + * Returns the refunds stored in the transaction + * @return array|null + */ + public function getRefunds() + { + return $this->_refunds; + } + + /** + * Sets the refunds stored in the transaction + * @param array $refunds + * @return \Paymill\Models\Response\Transaction + */ + public function setRefunds($refunds) + { + $this->_refunds = $refunds; + return $this; + } + + /** + * @var string + */ + private $_currency; + + /** + * Returns the currency + * @return string + */ + public function getCurrency() + { + return $this->_currency; + } + + /** + * Sets the currency + * @param string $currency + * @return \Paymill\Models\Response\Transaction + */ + public function setCurrency($currency) + { + $this->_currency = $currency; + return $this; + } + + /** + * Response code for transaction feedback. 20000 marks a successful transaction + * @tutorial https://paymill.com/de-de/dokumentation/referenz/api-referenz/#document-statuscodes + * @var integer + */ + private $_responseCode; + + /** + * Returns the response code (20000 marks a successful transaction) + * @return integer + */ + public function getResponseCode() + { + return $this->_responseCode; + } + + /** + * Sets the response code of the transaction + * @param integer $responseCode + * @return \Paymill\Models\Response\Transaction + */ + public function setResponseCode($responseCode) + { + $this->_responseCode = $responseCode; + return $this; + } + + /** + * Unique identifier of this transaction provided to the acquirer for the statements. + * @var string + */ + private $_shortId; + + /** + * Returns the short id of the transaction + * @return string + */ + public function getShortId() + { + return $this->_shortId; + } + + /** + * Sets the transaction short id + * @param string $shortId + * @return \Paymill\Models\Response\Transaction + */ + public function setShortId($shortId) + { + $this->_shortId = $shortId; + return $this; + } + + /** + * PAYMILL invoice where the transaction fees are charged or null. + * @var array + */ + private $_invoices = null; + + /** + * Returns an array of invoices stored in the transaction + * @return array|null + */ + public function getInvoices() + { + return $this->_invoices; + } + + /** + * Stores an array of invoices in the transaction + * @param array $invoices + * @return \Paymill\Models\Response\Transaction + */ + public function setInvoices($invoices) + { + $this->_invoices = $invoices; + return $this; + } + + /** + * @var \Paymill\Models\Response\Payment + */ + private $_payment; + + /** + * Returns the payment associated with the transaction + * @return \Paymill\Models\Response\Payment + */ + public function getPayment() + { + return $this->_payment; + } + + /** + * Sets the Payment for the transcation + * @param \Paymill\Models\Response\Payment $payment + * @return \Paymill\Models\Response\Transaction + */ + public function setPayment($payment) + { + $this->_payment = $payment; + return $this; + } + + /** + * @var \Paymill\Models\Response\Client + */ + private $_client = null; + + /** + * Returns the Client associated with the transaction. If no client is available null will be returned + * @return \Paymill\Models\Response\Client|null + */ + public function getClient() + { + return $this->_client; + } + + /** + * Sets the Client for the transaction + * @param \Paymill\Models\Response\Client $client + * @return \Paymill\Models\Response\Transaction + */ + public function setClient($client) + { + $this->_client = $client; + return $this; + } + + /** + * @var \Paymill\Models\Response\Preauthorization + */ + private $_preauthorization = null; + + /** + * Returns the Preauthorization associated with the transaction. If no preAuth is available null will be returned + * @return \Paymill\Models\Response\Preauthorization|null + */ + public function getPreauthorization() + { + return $this->_preauthorization; + } + + /** + * Sets the Preauthorization for the transaction + * @param \Paymill\Models\Response\Preauthorization $preauthorization + * @return \Paymill\Models\Response\Transaction + */ + public function setPreauthorization($preauthorization) + { + $this->_preauthorization = $preauthorization; + return $this; + } + + /** + * @var array + */ + private $_fees; + + /** + * Returns the fee array stored in the transaction + * @return array + */ + public function getFees() + { + return $this->_fees; + } + + /** + * Sets the Fees array for the transaction + * @param array $fees + * @return \Paymill\Models\Response\Transaction + */ + public function setFees($fees) + { + $this->_fees = $fees; + return $this; + } +} \ No newline at end of file diff --git a/lib/api/lib/Paymill/Models/Response/Webhook.php b/lib/api/lib/Paymill/Models/Response/Webhook.php new file mode 100644 index 0000000..bf514c1 --- /dev/null +++ b/lib/api/lib/Paymill/Models/Response/Webhook.php @@ -0,0 +1,122 @@ +_url; + } + + /** + * Sets the webhook url + * @param string $url + * @return \Paymill\Models\Response\Webhook + */ + public function setUrl($url) + { + $this->_url = $url; + return $this; + } + + /** + * Returns the livemode flag of the webhook + * @return boolean + */ + public function getLivemode() + { + return $this->_livemode; + } + + /** + * Sets the livemode flag of the webhook + * @param boolean $livemode + * @return \Paymill\Models\Response\Webhook + */ + public function setLivemode($livemode) + { + $this->_livemode = $livemode; + return $this; + } + + /** + * Returns the event types as an array + * @return array + */ + public function getEventTypes() + { + return $this->_eventTypes; + } + + /** + * Sets the event types for the webhook. + * There are a number of events you can react to. Each webhook can be configured to catch any kind of event + * individually, so you can create different webhooks for different events. Each Webhook needs to be attached + * to at least one event. For example the event subscription.succeeded is triggered every time a successful + * transaction has been made in our system that is based on a subscription. Shortly after that has been triggered, + * we will call every webhook you defined for this event and send detailed information to it. + * @tutorial https://paymill.com/de-de/dokumentation/referenz/api-referenz/#document-webhooks + * @param array $eventTypes + * @return \Paymill\Models\Response\Webhook + */ + public function setEventTypes($eventTypes) + { + $this->_eventTypes = $eventTypes; + return $this; + } + + /** + * Returns the email registered for this webhook + * @return string||null + */ + public function getEmail() + { + return $this->_email; + } + + /** + * Sets the email for the webhook. + * @param string $email Instead of setting the url parameter you can set the email parameter to create a webhook, + * where we send mails to in case of an event. + * @return \Paymill\Models\Response\Webhook + */ + public function setEmail($email) + { + $this->_email = $email; + return $this; + } + +} \ No newline at end of file diff --git a/lib/api/lib/Paymill/Request.php b/lib/api/lib/Paymill/Request.php new file mode 100644 index 0000000..ef2e5ed --- /dev/null +++ b/lib/api/lib/Paymill/Request.php @@ -0,0 +1,261 @@ +setConnectionClass(new Curl($privateKey)); + } + } + + /** + * @param \Paymill\API\CommunicationAbstract|Curl $communicationClass + * @return $this + */ + public function setConnectionClass(CommunicationAbstract $communicationClass = null) + { + $this->_connectionClass = $communicationClass; + return $this; + } + + /** + * Sends a creation request using the provided model + * @param \Paymill\Models\Request\Base $model + * @throws PaymillException + * @return \Paymill\Models\Response\Base + */ + public function create($model) + { + return $this->_request($model, __FUNCTION__); + } + + /** + * Sends an update request using the provided model + * @param \Paymill\Models\Request\Base $model + * @throws PaymillException + * @return \Paymill\Models\Response\Base + */ + public function update($model) + { + return $this->_request($model, __FUNCTION__); + } + + /** + * Sends a delete request using the provided model + * @param \Paymill\Models\Request\Base $model + * @throws PaymillException + * @return \Paymill\Models\Response\Base + */ + public function delete($model) + { + return $this->_request($model, __FUNCTION__); + } + + /** + * Sends a getAll request using the provided model + * @param \Paymill\Models\Request\Base $model + * @throws PaymillException + * @return array + */ + public function getAll($model) + { + return $this->_request($model, __FUNCTION__); + } + + /** + * Sends a getOne request using the provided model + * @param \Paymill\Models\Request\Base $model + * @throws PaymillException + * @return \Paymill\Models\Response\Base + */ + public function getOne($model) + { + return $this->_request($model, __FUNCTION__); + } + + /** + * Returns the response of the last request + * @return array + */ + public function getLastResponse() + { + return $this->_lastResponse; + } + + /** + * Returns the parameter which were used for the last request + * @return array + */ + public function getLastRequest() + { + return $this->_lastRequest; + } + + /** + * Returns the Version of this Lib + * + * @return string + */ + public function getVersion(){ + return $this->_version; + } + + /** + * Returns the LastResponse as StdClassObject. Returns false if no request was made earlier. + * + * @return false | stdClass + */ + public function getJSONObject(){ + $result = false; + $responseHandler = new ResponseHandler(); + if(is_array($this->_lastResponse)){ + $result = $responseHandler->arrayToObject($this->_lastResponse['body']); + } + return $result; + } + + /** + * + * @param string $method + * @return string + */ + private function _getHTTPMethod($method) + { + $httpMethod = 'POST'; + switch ($method) { + case 'create': + $httpMethod = 'POST'; + break; + case 'update': + $httpMethod = 'PUT'; + break; + case 'delete': + $httpMethod = 'DELETE'; + break; + case 'getAll': + case 'getOne': + $httpMethod = 'GET'; + break; + } + return $httpMethod; + } + + /** + * Sends a request based on the provided request model and according to the argumented method + * @param \Paymill\Models\Request\Base $model + * @param string $method (Create, update, delete, getAll, getOne) + * @throws PaymillException + * @return \Paymill\Models\Response\Base|\Paymill\Models\Response\Error + */ + private function _request(Base $model, $method) + { + if(!is_a($this->_connectionClass, '\Paymill\API\CommunicationAbstract')){ + throw new PaymillException(null,'The connenction class is missing!'); + } + $httpMethod = $this->_getHTTPMethod($method); + $parameter = $model->parameterize($method); + $serviceResource = $model->getServiceResource() . $model->getId(); + if(is_a($model, "\Paymill\Models\Request\Transaction") && $method === "create"){ + $source = empty($this->_source) ? "PhpLib" . $this->getVersion(): "PhpLib" . $this->getVersion() . "_" . $this->getSource(); + $parameter['source'] = $source; + } + + try { + $this->_lastRequest = $parameter; + $response = $this->_connectionClass->requestApi( + $serviceResource, $parameter, $httpMethod + ); + $this->_lastResponse = $response; + $responseHandler = new ResponseHandler(); + if ($method === 'getAll') { + if ($responseHandler->validateResponse($response)) { + $convertedResponse = $response['body']['data']; + } else { + $convertedResponse = $responseHandler->convertResponse($response, $model->getServiceResource()); + } + } else { + $convertedResponse = $responseHandler->convertResponse($response, $model->getServiceResource()); + } + } catch (\Exception $e) { + $errorModel = new Error(); + $convertedResponse = $errorModel->setErrorMessage($e->getMessage()); + } + + if (is_a($convertedResponse, '\Paymill\Models\Response\Error')) { + throw new PaymillException( + $convertedResponse->getResponseCode(), $convertedResponse->getErrorMessage(), $convertedResponse->getHttpStatusCode() + ); + } + + return $convertedResponse; + } + + /** + * Sets the source for requests + * + * @param string $source + * @return \Paymill\Request + */ + public function setSource($source){ + if(is_string($source)){ + $this->_source = $source; + } + return $this; + } + + /** + * Returns the source for requests + * + * @return string + */ + public function getSource(){ + return $this->_source; + } + +} diff --git a/lib/api/lib/Paymill/Services/PaymillException.php b/lib/api/lib/Paymill/Services/PaymillException.php new file mode 100644 index 0000000..dad18c8 --- /dev/null +++ b/lib/api/lib/Paymill/Services/PaymillException.php @@ -0,0 +1,56 @@ +_errorMessage = $message; + $this->_responseCode = $responseCode; + $this->_httpStatusCode = $code; + } + + /** + * @return string + */ + public function getErrorMessage() + { + return $this->_errorMessage; + } + + /** + * @return string + */ + public function getStatusCode() + { + return $this->_httpStatusCode; + } + + /** + * @return integer + */ + public function getResponseCode() + { + return $this->_responseCode; + } + +} diff --git a/lib/api/lib/Paymill/Services/ResponseHandler.php b/lib/api/lib/Paymill/Services/ResponseHandler.php new file mode 100644 index 0000000..070fe3c --- /dev/null +++ b/lib/api/lib/Paymill/Services/ResponseHandler.php @@ -0,0 +1,414 @@ + "General undefined response.", + 10002 => "Still waiting on something.", + 20000 => "General success response.", + 40000 => "General problem with data.", + 40001 => "General problem with payment data.", + 40100 => "Problem with credit card data.", + 40101 => "Problem with cvv.", + 40102 => "Card expired or not yet valid.", + 40103 => "Limit exceeded.", + 40104 => "Card invalid.", + 40105 => "Expiry date not valid.", + 40106 => "Credit card brand required.", + 40200 => "Problem with bank account data.", + 40201 => "Bank account data combination mismatch.", + 40202 => "User authentication failed.", + 40300 => "Problem with 3d secure data.", + 40301 => "Currency / amount mismatch", + 40400 => "Problem with input data.", + 40401 => "Amount too low or zero.", + 40402 => "Usage field too long.", + 40403 => "Currency not allowed.", + 50000 => "General problem with backend.", + 50001 => "Country blacklisted.", + 50100 => "Technical error with credit card.", + 50101 => "Error limit exceeded.", + 50102 => "Card declined by authorization system.", + 50103 => "Manipulation or stolen card.", + 50104 => "Card restricted.", + 50105 => "Invalid card configuration data.", + 50200 => "Technical error with bank account.", + 50201 => "Card blacklisted.", + 50300 => "Technical error with 3D secure.", + 50400 => "Decline because of risk issues.", + 50500 => "General timeout.", + 50501 => "Timeout on side of the acquirer.", + 50502 => "Risk management transaction timeout.", + 50600 => "Duplicate transaction.", + ); + + /** + * Converts a response to a model + * @param array $response + * @param string $serviceResource + * @return Models\Base|Error + */ + public function convertResponse($response, $serviceResource) + { + $resourceName = substr($serviceResource, 0, -2); + $resultValue = null; + if ($this->validateResponse($response)) { + $resultValue = $this->_convertResponseToModel($response['body']['data'], $resourceName); + } else { + $resultValue = $this->_convertErrorToModel($response); + } + return $resultValue; + } + + /** + * Creates an object from a response array based on the call-context + * @param array $response Response from any Request + * @param string $resourceName + * @return Models\Base + */ + private function _convertResponseToModel($response, $resourceName) + { + if (!is_array($response) || empty($response)) { + return $response; + } + + $model = null; + switch (strtolower($resourceName)) { + case 'client': + $model = $this->_createClient($response); + break; + case 'payment': + $model = $this->_createPayment($response); + break; + case 'transaction': + $model = $this->_createTransaction($response); + break; + case 'preauthorization': + if (isset($response['preauthorization'])) { + $response = $response['preauthorization']; + } + $model = $this->_createPreauthorization($response); + break; + case 'refund': + $model = $this->_createRefund($response); + break; + case 'offer': + $model = $this->_createOffer($response); + break; + case 'subscription': + $model = $this->_createSubscription($response); + break; + case 'webhook': + $model = $this->_createWebhook($response); + break; + } + + return $model; + } + + /** + * Creates and fills a clientmodel + * + * @param array $response + * @return \Paymill\Models\Response\Client + */ + private function _createClient($response) + { + $model = new Models\Client(); + $model->setId($response['id']); + $model->setEmail($response['email']); + $model->setDescription($response['description']); + $model->setCreatedAt($response['created_at']); + $model->setUpdatedAt($response['updated_at']); + $model->setSubscription($this->_handleRecursive($response['subscription'], 'subscription')); + $model->setAppId($response['app_id']); + $model->setPayment($this->_handleRecursive($response['payment'], 'payment')); + return $model; + } + + /** + * Creates and fills a paymentmodel + * + * @param array $response + * @return \Paymill\Models\Response\Payment + */ + private function _createPayment($response) + { + $model = new Models\Payment(); + $model->setId($response['id']); + $model->setType($response['type']); + $model->setClient($this->_convertResponseToModel($response['client'], "client")); + if ($response['type'] === "creditcard") { + $model->setCardType($response['card_type']); + $model->setCountry($response['country']); + $model->setExpireMonth($response['expire_month']); + $model->setExpireYear($response['expire_year']); + $model->setCardHolder($response['card_holder']); + $model->setLastFour($response['last4']); + } else if ($response['type'] === "debit") { + $model->setHolder($response['holder']); + $model->setCode($response['code']); + $model->setAccount($response['account']); + $model->setBic($response['bic']); + $model->setIban($response['iban']); + } + $model->setCreatedAt($response['created_at']); + $model->setUpdatedAt($response['updated_at']); + $model->setAppId($response['app_id']); + return $model; + } + + /** + * Creates and fills a transactionmodel + * + * @param array $response + * @return \Paymill\Models\Response\Transaction + */ + private function _createTransaction($response) + { + $model = new Models\Transaction(); + $model->setId($response['id']); + $model->setAmount($response['amount']); + $model->setOriginAmount($response['origin_amount']); + $model->setStatus($response['status']); + $model->setDescription($response['description']); + $model->setLivemode($response['livemode']); + $model->setRefunds($this->_handleRecursive($response['refunds'], 'refund')); + $model->setCurrency($response['currency']); + $model->setCreatedAt($response['created_at']); + $model->setUpdatedAt($response['updated_at']); + $model->setResponseCode($response['response_code']); + $model->setShortId($response['short_id']); + $model->setInvoices($response['invoices']); + $model->setPayment($this->_convertResponseToModel($response['payment'], "payment")); + $model->setClient($this->_convertResponseToModel($response['client'], "client")); + $model->setPreauthorization($this->_convertResponseToModel($response['preauthorization'], "preauthorization")); + $model->setFees($response['fees']); + $model->setAppId($response['app_id']); + return $model; + } + + /** + * Creates and fills a preauthorizationmodel + * + * @param array $response + * @return \Paymill\Models\Response\Preauthorization + */ + private function _createPreauthorization($response) + { + $model = new Models\Preauthorization(); + $model->setId($response['id']); + $model->setAmount($response['amount']); + $model->setCurrency($response['currency']); + $model->setStatus($response['status']); + $model->setLivemode($response['livemode']); + $model->setCreatedAt($response['created_at']); + $model->setUpdatedAt($response['updated_at']); + $model->setPayment($this->_convertResponseToModel($response['payment'], "payment")); + $model->setClient($this->_convertResponseToModel($response['client'], "client")); + $model->setAppId($response['app_id']); + return $model; + } + + /** + * Creates and fills a refundmodel + * + * @param array $response + * @return \Paymill\Models\Response\Refund + */ + private function _createRefund($response) + { + $model = new Models\Refund(); + $model->setId($response['id']); + $model->setAmount($response['amount']); + $model->setStatus($response['status']); + $model->setDescription($response['description']); + $model->setLivemode($response['livemode']); + $model->setCreatedAt($response['created_at']); + $model->setUpdatedAt($response['updated_at']); + $model->setResponseCode($response['response_code']); + //Refund doesn't have the array index 'transaction' when using getOne + $model->setTransaction(isset($response['transaction']) ? $this->_convertResponseToModel($response['transaction'], 'transaction') : null); + $model->setAppId($response['app_id']); + return $model; + } + + /** + * Creates and fills a offermodel + * + * @param array $response + * @return \Paymill\Models\Response\Offer + */ + private function _createOffer($response) + { + $model = new Models\Offer(); + $model->setId($response['id']); + $model->setName($response['name']); + $model->setAmount($response['amount']); + $model->setCurrency($response['currency']); + $model->setInterval($response['interval']); + $model->setTrialPeriodDays($response['trial_period_days']); + $model->setCreatedAt($response['created_at']); + $model->setUpdatedAt($response['updated_at']); + $model->setSubscriptionCount($response['subscription_count']['active'], $response['subscription_count']['inactive']); + $model->setAppId($response['app_id']); + return $model; + } + + /** + * Creates and fills a subscriptionmodel + * + * @param array $response + * @return \Paymill\Models\Response\Subscription + */ + private function _createSubscription($response) + { + $model = new Models\Subscription(); + $model->setId($response['id']); + $model->setOffer($this->_convertResponseToModel($response['offer'], 'offer')); + $model->setLivemode($response['livemode']); + $model->setCancelAtPeriodEnd($response['cancel_at_period_end']); + $model->setTrialStart($response['trial_start']); + $model->setTrialEnd($response['trial_end']); + $model->setNextCaptureAt($response['next_capture_at']); + $model->setCreatedAt($response['created_at']); + $model->setUpdatedAt($response['updated_at']); + $model->setCanceledAt($response['canceled_at']); + $model->setPayment($this->_convertResponseToModel($response['payment'], "payment")); + $model->setClient($this->_convertResponseToModel($response['client'], "client")); + $model->setAppId($response['app_id']); + return $model; + } + + /** + * Creates and fills a webhookmodel + * + * @param array $response + * @return \Paymill\Models\Response\Webhook + */ + private function _createWebhook($response) + { + $model = new Models\Webhook(); + $model->setId($response['id']); + isset($response['url']) ? $model->setUrl($response['url']) : $model->setEmail($response['email']); + $model->setLivemode($response['livemode']); + $model->setEventTypes($response['event_types']); + $model->setCreatedAt($response['created_at']); + $model->setUpdatedAt($response['updated_at']); + $model->setAppId($response['app_id']); + return $model; + } + + /** + * Handles the multidimensional param arrays during model creation + * @param array $response + * @param string $resourceName + * @return array|null|Models\Base + */ + private function _handleRecursive($response, $resourceName) + { + $result = null; + if (isset($response['id'])) { + $result = $this->_convertResponseToModel($response, $resourceName); + } else if (!is_null($response)) { + $paymentArray = array(); + foreach ($response as $paymentData) { + array_push($paymentArray, $this->_convertResponseToModel($paymentData, $resourceName)); + } + $result = $paymentArray; + } + return $result; + } + + /** + * Generates an error model based on the provided response array + * @param array $response + * @return Error + */ + private function _convertErrorToModel($response) + { + $errorModel = new Error(); + + $httpStatusCode = isset($response['header']['status']) ? $response['header']['status'] : null; + $errorModel->setHttpStatusCode($httpStatusCode); + + $responseCode = isset($response['body']['data']['response_code']) ? $response['body']['data']['response_code'] : null; + $errorModel->setResponseCode($responseCode); + + $errorCode = 'Undefined Error. This should not happen!'; + if (isset($this->_errorCodes[$responseCode])) { + $errorCode = $this->_errorCodes[$responseCode]; + } + + if (isset($response['body'])) { + if (is_array($response['body'])) { + if (isset($response['body']['error'])) { + if (is_array($response['body']['error'])) { + $errorCode = $this->getErrorMessageFromArray($response['body']['error']); + } elseif (is_string($response['body']['error'])) { + $errorCode = $response['body']['error']; + } + } + } elseif (is_string($response['body'])) { + $json = json_decode($response['body']); + $errorCode = $json->error; + } + } + $errorModel->setErrorMessage($errorCode); + return $errorModel; + } + + /** + * Validates the data responsed by the API + * + * Only Refund, Transaction and Preauthorization return an response_code + * @param array $response + * @return boolean + */ + public function validateResponse($response) + { + $returnValue = false; + if ($response['header']['status'] === 200) { + if (isset($response['body']['data']['response_code'])) { + $returnValue = false; + if ($response['body']['data']['response_code'] === 20000) { + $returnValue = true; + } + } else { + $returnValue = true; + } + } + return $returnValue; + } + + private function getErrorMessageFromArray($errorArray) + { + $errorMessage = array_shift($errorArray); + if (is_array($errorMessage)) { + return $this->getErrorMessageFromArray($errorMessage); + } else { + return $errorMessage; + } + } + + /** + * Converts an array into an object + * + * @param array $array + * @return stdClass + */ + public function arrayToObject($array) + { + return is_array($array) ? (object) array_map(array($this, 'arrayToObject'), $array) : $array; + } + +} diff --git a/lib/api/tests/bootstrap.php b/lib/api/tests/bootstrap.php new file mode 100644 index 0000000..c86feed --- /dev/null +++ b/lib/api/tests/bootstrap.php @@ -0,0 +1,21 @@ +_service = new Request(); + $this->_service->setConnectionClass(new Curl(API_TEST_KEY)); + $this->_model = new Models\Request\Client(); + parent::setUp(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_service = null; + $this->_model = null; + parent::tearDown(); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function createClient() + { + $this->_model->setEmail('Plugins@Paymill.de') + ->setDescription('Test'); + $result = $this->_service->create($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Client', $result); + return $result; + } + + /** + * @test + * @codeCoverageIgnore + * @depends createClient + */ + public function updateClient($model) + { + $this->_model->setId($model->getId()) + ->setDescription('UpdateSuccessful'); + $result = $this->_service->update($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Client', $result, var_export($result, true)); + $this->assertEquals('UpdateSuccessful', $result->getDescription()); + } + + /** + * @test + * @codeCoverageIgnore + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Client not found + */ + public function updateClientWithWrongId() + { + $this->_model->setId('YouWillNeverFindMe404') + ->setDescription('TEST'); + $this->_service->update($this->_model); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createClient + */ + public function getOneClient($model) + { + $this->_model->setId($model->getId()); + $result = $this->_service->getOne($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Client', $result, var_export($result, true)); + $this->assertEquals($model->getId(), $result->getId()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createClient + */ + public function getAllClient() + { + $result = $this->_service->getAll($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function getAllClientWithFilter() + { + $this->_model->setFilter(array( + 'count' => 2, + 'offset' => 0 + ) + ); + $result = $this->_service->getAll($this->_model); + $this->assertEquals(2, count($result), var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createClient + * @depends getOneClient + * @depends updateClient + */ + public function deleteClient($model) + { + $this->_model->setId($model->getId()); + $this->markTestIncomplete('Client does not return a empty array like the other resources.'); + $result = $this->_service->delete($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage 'PluginsAtPaymillDotde' is not a valid email address. + */ + public function createClientWithInvalidEmail() + { + $this->_model->setEmail('PluginsAtPaymillDotde') + ->setDescription('Test'); + $result = $this->_service->create($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Client', $result); + return $result; + } + + +} diff --git a/lib/api/tests/integration/OfferTest.php b/lib/api/tests/integration/OfferTest.php new file mode 100644 index 0000000..c9d3644 --- /dev/null +++ b/lib/api/tests/integration/OfferTest.php @@ -0,0 +1,129 @@ +_service = new Request(); + $this->_service->setConnectionClass(new Curl(API_TEST_KEY)); + $this->_model = new Models\Request\Offer(); + parent::setUp(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_service = null; + $this->_model = null; + parent::tearDown(); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function createOffer() + { + $this->_model->setAmount(100) + ->setCurrency('EUR') + ->setInterval('2 DAY') + ->setName('TestOffer'); + $result = $this->_service->create($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Offer', $result, var_export($result, true)); + return $result; + } + + /** + * @test + * @codeCoverageIgnore + * @depends createOffer + */ + public function updateOffer($model) + { + $this->_model->setId($model->getId()) + ->setName('NewName'); + $result = $this->_service->update($this->_model); + + $this->assertInstanceOf('Paymill\Models\Response\Offer', $result, var_export($result, true)); + $this->assertEquals($model->getId(), $result->getId()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createOffer + */ + public function getOneOffer($model) + { + $this->_model->setId($model->getId()); + $this->assertInstanceOf('Paymill\Models\Response\Offer', $result = $this->_service->getOne($this->_model), var_export($result, true)); + $this->assertEquals($model->getId(), $result->getId()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createOffer + */ + public function getAllOffer() + { + $this->_model; + $result = $this->_service->getAll($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function getAllOfferWithFilter() + { + $this->_model->setFilter(array( + 'count' => 2, + 'offset' => 0 + ) + ); + $result = $this->_service->getAll($this->_model); + $this->assertEquals(2, count($result), var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createOffer + * @depends getOneOffer + * @depends updateOffer + */ + public function deleteOffer($model) + { + $this->_model->setId($model->getId()); + $result = $this->_service->delete($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + +} diff --git a/lib/api/tests/integration/PaymentTest.php b/lib/api/tests/integration/PaymentTest.php new file mode 100644 index 0000000..f098689 --- /dev/null +++ b/lib/api/tests/integration/PaymentTest.php @@ -0,0 +1,125 @@ +_service = new Request(); + $this->_service->setConnectionClass(new Curl(API_TEST_KEY)); + $this->_model = new Models\Request\Payment(); + parent::setUp(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_service = null; + $this->_model = null; + parent::tearDown(); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function createPayment() + { + $this->_model->setToken("098f6bcd4621d373cade4e832627b4f6"); + $result = $this->_service->create($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Payment', $result); + return $result; + } + + /** + * @test + * @codeCoverageIgnore + * @depends createPayment + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Method not Found + */ + public function updatePayment($model) + { + $this->_model->setId($model->getId()); + $this->_service->update($this->_model); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createPayment + */ + public function getOnePayment($model) + { + $this->_model->setId($model->getId()); + $result = $this->_service->getOne($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Payment', $result, var_export($result, true)); + $this->assertEquals($model->getId(), $result->getId()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createPayment + */ + public function getAllPayment() + { + $this->_model; + $result = $this->_service->getAll($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function getAllPaymentWithFilter() + { + $this->_model->setFilter(array( + 'count' => 2, + 'offset' => 0 + ) + ); + $result = $this->_service->getAll($this->_model); + $this->assertEquals(2, count($result), var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createPayment + * @depends getOnePayment + * @depends updatePayment + */ + public function deletePayment($model) + { + $this->_model->setId($model->getId()); + $result = $this->_service->delete($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + +} diff --git a/lib/api/tests/integration/PreauthorizationTest.php b/lib/api/tests/integration/PreauthorizationTest.php new file mode 100644 index 0000000..054e822 --- /dev/null +++ b/lib/api/tests/integration/PreauthorizationTest.php @@ -0,0 +1,131 @@ +_service = new Request(); + $this->_service->setConnectionClass(new Curl(API_TEST_KEY)); + $this->_model = new Models\Request\Preauthorization(); + parent::setUp(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_service = null; + $this->_model = null; + parent::tearDown(); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function createPreauthorization() + { + $this->_model->setToken("098f6bcd4621d373cade4e832627b4f6") + ->setAmount(100) + ->setCurrency('EUR'); + $result = $this->_service->create($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Preauthorization', $result); + return $result; + } + + /** + * @test + * @codeCoverageIgnore + * @depends createPreauthorization + */ + public function updatePreauthorization($model) + { + $this->_model->setId($model->getId()); + $this->markTestIncomplete( + 'Preauthorization should return a valid Object like Client "Method not found". Returns a empty 500-Response instead.' + ); + + $result = $this->_service->update($this->_model); + + $this->assertInstanceOf('Paymill\Models\Response\Error', $result, var_export($result, true)); + $this->assertEquals('Preauthorization was not found', $result->getErrorMessage()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createPreauthorization + */ + public function getOnePreauthorization($model) + { + $this->_model->setId($model->getId()); + $this->assertInstanceOf('Paymill\Models\Response\Preauthorization', $result = $this->_service->getOne($this->_model), var_export($result, true)); + $this->assertEquals($model->getId(), $result->getId()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createPreauthorization + */ + public function getAllPreauthorization() + { + $this->_model; + $result = $this->_service->getAll($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function getAllPreauthorizationWithFilter() + { + $this->_model->setFilter(array( + 'count' => 2, + 'offset' => 0 + ) + ); + $result = $this->_service->getAll($this->_model); + $this->assertEquals(2, count($result), var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createPreauthorization + * @depends getOnePreauthorization + * @depends updatePreauthorization + */ + public function deletePreauthorization($model) + { + $this->_model->setId($model->getId()); + $result = $this->_service->delete($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + +} diff --git a/lib/api/tests/integration/RefundTest.php b/lib/api/tests/integration/RefundTest.php new file mode 100644 index 0000000..a72a401 --- /dev/null +++ b/lib/api/tests/integration/RefundTest.php @@ -0,0 +1,136 @@ +_service = new Request(); + $this->_service->setConnectionClass(new Curl(API_TEST_KEY)); + $this->_model = new Models\Request\Refund(); + parent::setUp(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_service = null; + $this->_model = null; + parent::tearDown(); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function createRefund() + { + $transactionModel = new Models\Request\Transaction(); + $transactionModel->setAmount(200) + ->setCurrency('EUR') + ->setToken("098f6bcd4621d373cade4e832627b4f6"); + $transactionModelResponse = $this->_service->create($transactionModel); + $this->assertInstanceOf('Paymill\Models\Response\Transaction', $transactionModelResponse, var_export($transactionModelResponse, true)); + + $this->_model->setAmount(100) + ->setDescription('EUR') + ->setId($transactionModelResponse->getId()); + $result = $this->_service->create($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Refund', $result, var_export($result, true)); + return $result; + } + + /** + * @test + * @codeCoverageIgnore + * @depends createRefund + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Method not Found + */ + public function updateRefund($model) + { + $this->_model->setId($model->getId()); + $this->_service->update($this->_model); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createRefund + */ + public function getOneRefund($model) + { + $this->_model->setId($model->getId()); + $this->assertInstanceOf('Paymill\Models\Response\Refund', $result = $this->_service->getOne($this->_model), var_export($result, true)); + $this->assertEquals($model->getId(), $result->getId()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createRefund + */ + public function getAllRefund() + { + $this->_model; + $result = $this->_service->getAll($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function getAllRefundWithFilter() + { + $this->_model->setFilter(array( + 'count' => 2, + 'offset' => 0 + ) + ); + $result = $this->_service->getAll($this->_model); + $this->assertEquals(2, count($result), var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createRefund + * @depends getOneRefund + * @depends updateRefund + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Method not Found + */ + public function deleteRefund($model) + { + $this->_model->setId($model->getId()); + $result = $this->_service->delete($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Error', $result, var_export($result, true)); + $this->assertEquals('Method not Found', $result->getErrorMessage()); + } + +} diff --git a/lib/api/tests/integration/SubscriptionTest.php b/lib/api/tests/integration/SubscriptionTest.php new file mode 100644 index 0000000..8e60d35 --- /dev/null +++ b/lib/api/tests/integration/SubscriptionTest.php @@ -0,0 +1,149 @@ +_service = new Request(); + $this->_service->setConnectionClass(new Curl(API_TEST_KEY)); + $this->_model = new Models\Request\Subscription(); + parent::setUp(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_service = null; + $this->_model = null; + parent::tearDown(); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function createSubscription() + { + $this->markTestIncomplete( + 'Needs to be clarified with Paymill. Creation crashes with Message "currently there exists subscriptions, please delete them first"' + ); + + $OfferModel = new Models\Request\Offer(); + $OfferModel->setAmount(100) + ->setCurrency('EUR') + ->setInterval('2 DAY') + ->setName('TestOffer'); + $OfferModelResponse = $this->_service->create($OfferModel); + $this->assertInstanceOf('Paymill\Models\Response\Offer', $OfferModelResponse, var_export($OfferModelResponse, true)); + + $PaymentModel = new Models\Request\Payment(); + $PaymentModel->setToken("098f6bcd4621d373cade4e832627b4f6"); + $PaymentModelResponse = $this->_service->create($PaymentModel); + $this->assertInstanceOf('Paymill\Models\Response\Payment', $PaymentModelResponse, var_export($PaymentModelResponse, true)); + + $this->_model->setClient($PaymentModelResponse->getClient()) + ->setOffer($OfferModelResponse->getId()) + ->setPayment($PaymentModelResponse->getId()) + ->setCancelAtPeriodEnd(false); + $result = $this->_service->create($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Subscription', $result, var_export($result, true)); + $this->_service->delete($OfferModel->setId($OfferModelResponse->getId())); + $this->_service->delete($PaymentModel->setId($PaymentModelResponse->getId())); + return $result; + } + + /** + * @test + * @codeCoverageIgnore + * @depends createSubscription + */ + public function updateSubscription($model) + { + $this->_model->setId($model->getId()) + ->setCancelAtPeriodEnd(true); + $result = $this->_service->update($this->_model); + + $this->assertInstanceOf('Paymill\Models\Response\Subscription', $result, var_export($result, true)); + $this->assertTrue($result->getCancelAtPeriodEnd()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createSubscription + */ + public function getOneSubscription($model) + { + $this->_model->setId($model->getId()); + $this->assertInstanceOf('Paymill\Models\Response\Subscription', $result = $this->_service->getOne($this->_model), var_export($result, true)); + $this->assertEquals($model->getId(), $result->getId()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createSubscription + */ + public function getAllSubscription() + { + $this->_model; + $result = $this->_service->getAll($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function getAllSubscriptionWithFilter() + { + $this->_model->setFilter(array( + 'count' => 2, + 'offset' => 0 + ) + ); + $result = $this->_service->getAll($this->_model); + $this->assertEquals(2, count($result), var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createSubscription + * @depends getOneSubscription + * @depends updateSubscription + */ + public function deleteSubscription($model) + { + $this->_model->setId($model->getId()); + $this->markTestIncomplete('Subscription does not return a empty array like the other resources.'); + $result = $this->_service->delete($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + +} diff --git a/lib/api/tests/integration/TransactionTest.php b/lib/api/tests/integration/TransactionTest.php new file mode 100644 index 0000000..c9979ac --- /dev/null +++ b/lib/api/tests/integration/TransactionTest.php @@ -0,0 +1,155 @@ +_service = new Request(); + $this->_service->setConnectionClass(new Curl(API_TEST_KEY)); + $this->_model = new Models\Request\Transaction(); + parent::setUp(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_service = null; + $this->_model = null; + parent::tearDown(); + } + + /** + * @test + * @codeCoverageIgnore + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Token, Payment or Preauthorization required + */ + public function createTransactionWithoutToken() + { + $this->_model->setAmount(100) + ->setCurrency('EUR'); + $this->_service->create($this->_model); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function createTransactionWithToken() + { + $this->_model->setAmount(100) + ->setCurrency('EUR') + ->setToken('098f6bcd4621d373cade4e832627b4f6'); + $result = $this->_service->create($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Transaction', $result); + return $result; + } + + /** + * @test + * @codeCoverageIgnore + * @depends createTransactionWithToken + */ + public function updateTransaction($model) + { + $this->_model->setId($model->getId()) + ->setDescription('TEST'); + $result = $this->_service->update($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Transaction', $result, var_export($result, true)); + $this->assertEquals('TEST', $result->getDescription()); + } + + /** + * @test + * @codeCoverageIgnore + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Transaction not found + */ + public function updateTransactionWithWrongId() + { + $this->_model->setId('YouWillNeverFindMe404') + ->setDescription('TEST'); + $this->_service->update($this->_model); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createTransactionWithToken + */ + public function getOneTransaction($model) + { + $this->_model->setId($model->getId()); + $result = $this->_service->getOne($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Transaction', $result, var_export($result, true)); + $this->assertEquals($model->getId(), $result->getId()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createTransactionWithToken + */ + public function getAllTransaction() + { + $this->_model; + $result = $this->_service->getAll($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function getAllTransactionWithFilter() + { + $this->_model->setFilter(array( + 'count' => 2, + 'offset' => 0 + ) + ); + $result = $this->_service->getAll($this->_model); + $this->assertEquals(2, count($result), var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createTransactionWithToken + * @depends getOneTransaction + * @depends updateTransaction + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Method not Found + */ + public function deleteTransaction($model) + { + $this->_model->setId($model->getId()); + $this->_service->delete($this->_model); + } + +} diff --git a/lib/api/tests/integration/WebhookTest.php b/lib/api/tests/integration/WebhookTest.php new file mode 100644 index 0000000..794a9c9 --- /dev/null +++ b/lib/api/tests/integration/WebhookTest.php @@ -0,0 +1,147 @@ +_service = new Request(); + $this->_service->setConnectionClass(new Curl(API_TEST_KEY)); + $this->_model = new Models\Request\Webhook(); + $this->_email = 'dummy@example.com'; + $this->_url = 'http://example.com/dummyCallback'; + parent::setUp(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_service = null; + $this->_model = null; + parent::tearDown(); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function createWebhookWithUrl() + { + $this->_model->setUrl('http://example.com/dummyCallback') + ->setEventTypes(array( + 'transaction.succeeded', 'subscription.created' + )); + $result = $this->_service->create($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Webhook', $result, var_export($result, true)); + return $result; + } + + /** + * @test + * @codeCoverageIgnore + */ + public function createWebhookWithEmail() + { + $this->_model->setEmail('dummy@example.com') + ->setEventTypes(array( + 'transaction.succeeded', 'subscription.created' + )); + $result = $this->_service->create($this->_model); + $this->assertInstanceOf('Paymill\Models\Response\Webhook', $result, var_export($result, true)); + $this->deleteWebhook($result); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createWebhookWithUrl + */ + public function updateWebhook($model) + { + $this->_model->setId($model->getId()) + ->setUrl('http://example.com/dummyCallbackUpdate'); + $result = $this->_service->update($this->_model); + + $this->assertInstanceOf('Paymill\Models\Response\Webhook', $result, var_export($result, true)); + $this->assertEquals($model->getId(), $result->getId()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createWebhookWithUrl + */ + public function getOneWebhook($model) + { + $this->_model->setId($model->getId()); + $this->assertInstanceOf('Paymill\Models\Response\Webhook', $result = $this->_service->getOne($this->_model), var_export($result, true)); + $this->assertEquals($model->getId(), $result->getId()); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createWebhookWithUrl + */ + public function getAllWebhook() + { + $this->_model; + $result = $this->_service->getAll($this->_model); + $this->assertInternalType('array', $result, var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + */ + public function getAllWebhookWithFilter() + { + $this->_model->setFilter(array( + 'count' => 2, + 'offset' => 0 + ) + ); + $result = $this->_service->getAll($this->_model); + $this->assertEquals(2, count($result), var_export($result, true)); + } + + /** + * @test + * @codeCoverageIgnore + * @depends createWebhookWithUrl + * @depends getOneWebhook + * @depends updateWebhook + */ + public function deleteWebhook($model) + { + $this->_model->setId($model->getId()); + $result = $this->_service->delete($this->_model); + $this->markTestIncomplete('Webhook does not return a empty array like the other resources. Returns Null instead!'); + $this->assertInternalType('array', $result, var_export($result, true)); + } + +} diff --git a/lib/api/tests/phpunit.xml.dist b/lib/api/tests/phpunit.xml.dist new file mode 100644 index 0000000..edcb437 --- /dev/null +++ b/lib/api/tests/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + ./integration + + + ./unit + + + + + + + diff --git a/lib/api/tests/unit/Paymill/API/CurlTest.php b/lib/api/tests/unit/Paymill/API/CurlTest.php new file mode 100644 index 0000000..c2e4b58 --- /dev/null +++ b/lib/api/tests/unit/Paymill/API/CurlTest.php @@ -0,0 +1,128 @@ +_curlObject = $this->getMock('Paymill\API\Curl', array('_curlExec', '_curlInfo', '_curlError'), array("TestToken")); + parent::setUp(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_curlObject = null; + parent::tearDown(); + } + + + /** + * Prepares the mocked curl object to return any desired value for the given method + * @param string $method + * @param mixed $response + */ + private function _setMockProperties($method, $response) + { + $this->_curlObject->expects($this->any())->method($method)->will($this->returnValue($response)); + } + + //Testmethods + + + /** + * Tests the requestApi function using GET as the httpMethod + * @test + */ + public function requestApiTestGET() + { + //Desired Results + $responseBody = array('test' => true); + $responseInfo = array('http_code' => 200, 'content_type' => 'test'); + + $this->_setMockProperties('_curlExec', $responseBody); + $this->_setMockProperties('_curlInfo', $responseInfo); + + //Testing method using GET + $result = $this->_curlObject->requestApi("", array(), 'GET'); + $this->assertEquals( + array( + 'header' => array( + 'status' => $responseInfo['http_code'], + 'reason' => null, + ), + 'body' => $responseBody) + ,$result); + } + + /** + * Tests the requestApi function using POST as the httpMethod + * @test + */ + public function requestApiTestPost() + { + //Desired Results + $responseBody = array('test' => true); + $responseInfo = array('http_code' => 200, 'content_type' => 'test'); + + $this->_setMockProperties('_curlExec', $responseBody); + $this->_setMockProperties('_curlInfo', $responseInfo); + + //using POST to test the else case + $result = $this->_curlObject->requestApi("", array(), 'POST'); + $this->assertEquals( + array( + 'header' => array( + 'status' => $responseInfo['http_code'], + 'reason' => null, + ), + 'body' => $responseBody) + ,$result); + } + + /** + * Tests the requestApi function triggering the error case + * @test + */ + public function requestApiTestError() + { + //Desired Results + $responseBody = false; + $responseInfo = array('http_code' => 666, 'content_type' => 'test'); + $responseError = array('test' => true); + + $this->_setMockProperties('_curlExec', $responseBody); + $this->_setMockProperties('_curlInfo', $responseInfo); + $this->_setMockProperties('_curlError', $responseError); + + //using POST to test the else case + $result = $this->_curlObject->requestApi("", array(), 'POST'); + $this->assertEquals( + array( + 'header' => array( + 'status' => $responseInfo['http_code'], + 'reason' => null, + ), + 'body' => array('error' => $responseError)) + ,$result); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Request/BaseTest.php b/lib/api/tests/unit/Paymill/Models/Request/BaseTest.php new file mode 100644 index 0000000..c899b04 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Request/BaseTest.php @@ -0,0 +1,67 @@ +_model = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $id = "This a weird test"; + + $this->_model = new Request\Client(); + $this->_model->setId($id); + $this->_model->setFilter(array('count' => 1)); + + $this->assertEquals($this->_model->getId(), $id); + $this->assertEquals($this->_model->getServiceResource(), "clients/"); + $this->assertEquals($this->_model->getFilter(), array('count' => 1)); + return $this->_model; + } + + /** + * Tests the parameter return from parameterize('getAll') + * @param \Paymill\Models\Request\Client $model + * @test + * @depends setGetTest + */ + public function parameterizeGetAll($model){ + $this->assertEquals($model->parameterize('getAll'), array('count' => 1)); + } +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Request/ClientTest.php b/lib/api/tests/unit/Paymill/Models/Request/ClientTest.php new file mode 100644 index 0000000..f4527a1 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Request/ClientTest.php @@ -0,0 +1,83 @@ +_client = new Request\Client(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_client = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $email = "lovely-client@example.com"; + $description = "Lovely Client"; + + $this->_client->setEmail($email)->setDescription($description); + + $this->assertEquals($this->_client->getEmail(), $email); + $this->assertEquals($this->_client->getDescription(), $description); + return $this->_client; + } + + /** + * Test the Parameterize function of the model + * @test + * @depends setGetTest + * @param \Paymill\Models\Request\Client $client + */ + public function parameterizeTest($client) + { + $testId = "client_88a388d9dd48f86c3136"; + $client->setId($testId); + + $creationArray = $client->parameterize("create"); + $updateArray = $client->parameterize("update"); + $getOneArray = $client->parameterize("getOne"); + + $this->assertEquals($creationArray, + array('email' => "lovely-client@example.com", 'description' => "Lovely Client")); + $this->assertEquals($updateArray, + array( + 'email' => 'lovely-client@example.com', + 'description' => 'Lovely Client' + )); + $this->assertEquals($getOneArray, array( + 'count' => 1, + 'offset' => 0 + )); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Request/OfferTest.php b/lib/api/tests/unit/Paymill/Models/Request/OfferTest.php new file mode 100644 index 0000000..a2cd40b --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Request/OfferTest.php @@ -0,0 +1,87 @@ +_offer = new Request\Offer(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_offer = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $amount = '4200'; + $currency = 'EUR'; + $interval = '1 MONTH'; + $name = 'Test Offer'; + + $this->_offer->setAmount($amount)->setCurrency($currency)->setInterval($interval)->setName($name); + + $this->assertEquals($this->_offer->getAmount(), $amount); + $this->assertEquals($this->_offer->getCurrency(), $currency); + $this->assertEquals($this->_offer->getInterval(), $interval); + $this->assertEquals($this->_offer->getName(), $name); + + return $this->_offer; + } + + /** + * Test the Parameterize function of the model + * @test + * @depends setGetTest + */ + public function parameterizeTest($offer) + { + $testId = "offer_88a388d9dd48f86c3136"; + $offer->setId($testId); + + $creationArray = $offer->parameterize("create"); + $updateArray = $offer->parameterize("update"); + $getOneArray = $offer->parameterize("getOne"); + + $this->assertEquals($creationArray, array( + 'amount' => '4200', // E.g. "4200" for 42.00 EUR + 'currency' => 'EUR', // ISO 4217 + 'interval' => '1 MONTH', + 'name' => 'Test Offer', + 'trial_period_days' => null + )); + $this->assertEquals($updateArray, array('name' => $offer->getName())); + $this->assertEquals($getOneArray, array( + 'count' => 1, + 'offset' => 0 + )); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Request/PaymentTest.php b/lib/api/tests/unit/Paymill/Models/Request/PaymentTest.php new file mode 100644 index 0000000..94ca655 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Request/PaymentTest.php @@ -0,0 +1,78 @@ +_payment = new Request\Payment(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_payment = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $sample = array( + 'client' => 'client_88a388d9dd48f86c3136', + 'token' => '098f6bcd4621d373cade4e832627b4f6' + ); + + $this->_payment->setClient($sample['client'])->setToken($sample['token']); + + $this->assertEquals($this->_payment->getClient(), $sample['client']); + $this->assertEquals($this->_payment->getToken(), $sample['token']); + + return $this->_payment; + } + + /** + * Test the Parameterize function of the model + * @test + * @depends setGetTest + */ + public function parameterizeTest($payment) + { + $testId = "payment_88a388d9dd48f86c3136"; + $payment->setId($testId); + + $creationArray = $payment->parameterize("create"); + $getOneArray = $payment->parameterize("getOne"); + + $this->assertEquals($creationArray, array('client' => 'client_88a388d9dd48f86c3136', 'token' => '098f6bcd4621d373cade4e832627b4f6') + ); + $this->assertEquals($getOneArray, array( + 'count' => 1, + 'offset' => 0 + )); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Request/PreauthorizationTest.php b/lib/api/tests/unit/Paymill/Models/Request/PreauthorizationTest.php new file mode 100644 index 0000000..39248f6 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Request/PreauthorizationTest.php @@ -0,0 +1,87 @@ +_preauthorization = new Request\Preauthorization(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_preauthorization = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $sample = array( + 'token' => '098f6bcd4621d373cade4e832627b4f6', + 'payment' => 'pay_d43cf0ee969d9847512b', + 'amount' => '4200', + 'currency' => 'EUR' + ); + + $this->_preauthorization->setPayment($sample['payment'])->setToken($sample['token'])->setAmount($sample['amount'])->setCurrency($sample['currency']); + + $this->assertEquals($this->_preauthorization->getToken(), $sample['token']); + $this->assertEquals($this->_preauthorization->getPayment(), $sample['payment']); + $this->assertEquals($this->_preauthorization->getAmount(), $sample['amount']); + $this->assertEquals($this->_preauthorization->getCurrency(), $sample['currency']); + + return $this->_preauthorization; + } + + /** + * Test the Parameterize function of the model + * @test + * @depends setGetTest + */ + public function parameterizeTest($preauthorization) + { + $testId = "preauthorization_88a388d9dd48f86c3136"; + $preauthorization->setId($testId); + + $creationArray = $preauthorization->parameterize("create"); + $getOneArray = $preauthorization->parameterize("getOne"); + + $this->assertEquals($creationArray, array( + 'payment' => 'pay_d43cf0ee969d9847512b', + 'amount' => '4200', + 'currency' => 'EUR' + ) + ); + $this->assertEquals($getOneArray, array( + 'count' => 1, + 'offset' => 0 + ) + ); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Request/RefundTest.php b/lib/api/tests/unit/Paymill/Models/Request/RefundTest.php new file mode 100644 index 0000000..c2c9c02 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Request/RefundTest.php @@ -0,0 +1,75 @@ +_refund = new Request\Refund(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_refund = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $sample = array( + 'amount' => '4200', // e.g. "4200" for 42.00 EUR + 'description' => 'Sample Description' + ); + + $this->_refund->setAmount($sample['amount']) + ->setDescription($sample['description']); + + $this->assertEquals($this->_refund->getAmount(), $sample['amount']); + $this->assertEquals($this->_refund->getDescription(), $sample['description']); + + return $this->_refund; + } + + /** + * Test the Parameterize function of the model + * @test + * @depends setGetTest + */ + public function parameterizeTest($refund) + { + $testId = "refund_88a388d9dd48f86c3136"; + $refund->setId($testId); + + $creationArray = $refund->parameterize("create"); + $getOneArray = $refund->parameterize("getOne"); + + $this->assertEquals($creationArray, array('amount' => '4200', 'description' => 'Sample Description')); + $this->assertEquals($getOneArray, array('count' => 1, 'offset' => 0)); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Request/SubscriptionTest.php b/lib/api/tests/unit/Paymill/Models/Request/SubscriptionTest.php new file mode 100644 index 0000000..1e5e369 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Request/SubscriptionTest.php @@ -0,0 +1,94 @@ +_subscription = new Request\Subscription(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_subscription = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $sample = array( + 'client' => 'client_88a388d9dd48f86c3136', + 'offer' => 'offer_40237e20a7d5a231d99b', + 'payment' => 'pay_95ba26ba2c613ebb0ca8' + ); + + $this->_subscription->setPayment($sample['payment'])->setOffer($sample['offer'])->setClient($sample['client']); + + $this->assertEquals($this->_subscription->getClient(), $sample['client']); + $this->assertEquals($this->_subscription->getOffer(), $sample['offer']); + $this->assertEquals($this->_subscription->getPayment(), $sample['payment']); + + + return $this->_subscription; + } + + /** + * Test the Parameterize function of the model + * @test + * @depends setGetTest + */ + public function parameterizeTest($subscription) + { + $testId = "subscription_88a388d9dd48f86c3136"; + $cancelAtPeriodEnd = true; + $subscription->setId($testId); + $subscription->setCancelAtPeriodEnd($cancelAtPeriodEnd); + + $creationArray = $subscription->parameterize("create"); + $updateArray = $subscription->parameterize("update"); + $getOneArray = $subscription->parameterize("getOne"); + + $this->assertEquals($creationArray, array( + 'client' => 'client_88a388d9dd48f86c3136', + 'offer' => 'offer_40237e20a7d5a231d99b', + 'payment' => 'pay_95ba26ba2c613ebb0ca8', + 'start_at' => null + )); + + $this->assertEquals($getOneArray, array( + 'count' => 1, + 'offset' => 0 + )); + $this->assertEquals($updateArray, array( + 'cancel_at_period_end' => true, + 'offer' => 'offer_40237e20a7d5a231d99b', + 'payment' => 'pay_95ba26ba2c613ebb0ca8' + )); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Request/TransactionTest.php b/lib/api/tests/unit/Paymill/Models/Request/TransactionTest.php new file mode 100644 index 0000000..f7562e8 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Request/TransactionTest.php @@ -0,0 +1,117 @@ +_transaction = new Request\Transaction(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_transaction = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $sample = array( + 'amount' => '4200', // e.g. "4200" for 42.00 EUR + 'currency' => 'EUR', // ISO 4217 + 'payment' => 'pay_2f82a672574647cd911d', + 'token' => '098f6bcd4621d373cade4e832627b4f6', + 'client' => 'client_c781b1d2f7f0f664b4d9', + 'preauthorization' => 'preauth_ec54f67e52e92051bd65', + 'fee_amount' => '420', // e.g. "420" for 4.20 EUR + 'fee_payment' => 'pay_098f6bcd4621d373cade4e832627b4f6', + 'fee_currency' => 'EUR', + 'description' => 'Test Transaction' + ); + + $this->_transaction + ->setAmount($sample['amount']) + ->setCurrency($sample['currency']) + ->setPayment($sample['payment']) + ->setToken($sample['token']) + ->setClient($sample['client']) + ->setPreauthorization($sample['preauthorization']) + ->setFeeAmount($sample['fee_amount']) + ->setFeePayment($sample['fee_payment']) + ->setFeeCurrency($sample['fee_currency']) + ->setDescription($sample['description']); + + $this->assertEquals($this->_transaction->getAmount(), $sample['amount']); + $this->assertEquals($this->_transaction->getCurrency(), $sample['currency']); + $this->assertEquals($this->_transaction->getPayment(), $sample['payment']); + $this->assertEquals($this->_transaction->getToken(), $sample['token']); + $this->assertEquals($this->_transaction->getClient(), $sample['client']); + $this->assertEquals($this->_transaction->getPreauthorization(), $sample['preauthorization']); + $this->assertEquals($this->_transaction->getFeeAmount(), $sample['fee_amount']); + $this->assertEquals($this->_transaction->getFeePayment(), $sample['fee_payment']); + $this->assertEquals($this->_transaction->getFeeCurrency(), $sample['fee_currency']); + $this->assertEquals($this->_transaction->getDescription(), $sample['description']); + + return $this->_transaction; + } + + /** + * Test the Parameterize function of the model + * @test + * @depends setGetTest + */ + public function parameterizeTest($transaction) + { + $testId = "transaction_88a388d9dd48f86c3136"; + $transaction->setId($testId); + + $creationArray = $transaction->parameterize("create"); + $updateArray = $transaction->parameterize("update"); + $getOneArray = $transaction->parameterize("getOne"); + + $this->assertEquals($creationArray, array( + 'amount' => '4200', // e.g. "4200" for 42.00 EUR + 'currency' => 'EUR', // ISO 4217 + 'client' => 'client_c781b1d2f7f0f664b4d9', + 'preauthorization' => 'preauth_ec54f67e52e92051bd65', + 'fee_amount' => '420', // e.g. "420" for 4.20 EUR + 'fee_payment' => 'pay_098f6bcd4621d373cade4e832627b4f6', + 'fee_currency' => 'EUR', + 'description' => 'Test Transaction' + )); + $this->assertEquals($updateArray, array( + 'description' => 'Test Transaction' + )); + $this->assertEquals($getOneArray, array( + 'count' => 1, + 'offset' => 0 + ) + ); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Request/WebhookTest.php b/lib/api/tests/unit/Paymill/Models/Request/WebhookTest.php new file mode 100644 index 0000000..12cac60 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Request/WebhookTest.php @@ -0,0 +1,87 @@ +_webhook = new Request\Webhook(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_webhook = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $sample = array( + 'url' => 'your-webhook-url', + 'email' => 'your-webhook-email', + 'event_types' => array('transaction.succeeded', 'subscription.created') + ); + + $this->_webhook + ->setUrl($sample['url']) + ->setEmail($sample['email']) + ->setEventTypes($sample['event_types']); + + $this->assertEquals($this->_webhook->getUrl(), $sample['url']); + $this->assertEquals($this->_webhook->getEmail(), $sample['email']); + $this->assertEquals($this->_webhook->getEventTypes(), $sample['event_types']); + + return $this->_webhook; + } + + /** + * Test the Parameterize function of the model + * @test + * @depends setGetTest + */ + public function parameterizeTest($webhook) + { + $testId = "webhook_88a388d9dd48f86c3136"; + $webhook->setId($testId); + + $creationArray = $webhook->parameterize("create"); + $updateArray = $webhook->parameterize("update"); + $getOneArray = $webhook->parameterize("getOne"); + + $this->assertEquals($creationArray, array( + 'url' => 'your-webhook-url', + 'event_types' => array('transaction.succeeded', 'subscription.created') + )); + $this->assertEquals($updateArray, array( + 'url' => 'your-webhook-url', + 'event_types' => array('transaction.succeeded', 'subscription.created') + )); + $this->assertEquals($getOneArray, array('count' => 1, 'offset' => 0)); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Response/BaseTest.php b/lib/api/tests/unit/Paymill/Models/Response/BaseTest.php new file mode 100644 index 0000000..bc1510c --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Response/BaseTest.php @@ -0,0 +1,58 @@ +_payment = new Response\Payment(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_payment = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $id = "This a weird test"; + $createdAt = 1; + $updatedAt = 2; + $appId = 1337; + + $this->_payment->setId($id)->setCreatedAt($createdAt)->setUpdatedAt($updatedAt)->setAppId($appId); + + $this->assertEquals($this->_payment->getId(), $id); + $this->assertEquals($this->_payment->getCreatedAt(), $createdAt); + $this->assertEquals($this->_payment->getUpdatedAt(), $updatedAt); + $this->assertEquals($this->_payment->getAppId(), $appId); + } +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Response/ClientTest.php b/lib/api/tests/unit/Paymill/Models/Response/ClientTest.php new file mode 100644 index 0000000..ba44bae --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Response/ClientTest.php @@ -0,0 +1,58 @@ +_client = new Response\Client(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_client = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $paymentModel = new Response\Payment(); + $email = "lovely-client@example.com"; + $descriptionValue = "TestDesc"; + + $this->_client->setEmail($email) + ->setDescription($descriptionValue) + ->setPayment($paymentModel); + + $this->assertEquals($this->_client->getEmail(), $email); + $this->assertEquals($this->_client->getDescription(), $descriptionValue); + $this->assertEquals($this->_client->getPayment(), $paymentModel); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Response/ErrorTest.php b/lib/api/tests/unit/Paymill/Models/Response/ErrorTest.php new file mode 100644 index 0000000..1c29adb --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Response/ErrorTest.php @@ -0,0 +1,50 @@ +setErrorMessage($errorMessage); + $errorModel->setHttpStatusCode($httpStatusCode); + $errorModel->setResponseCode($responseCode); + + $this->assertEquals($errorMessage, $errorModel->getErrorMessage()); + $this->assertEquals($httpStatusCode, $errorModel->getHttpStatusCode()); + $this->assertEquals($responseCode, $errorModel->getResponseCode()); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Response/OfferTest.php b/lib/api/tests/unit/Paymill/Models/Response/OfferTest.php new file mode 100644 index 0000000..8dd1a00 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Response/OfferTest.php @@ -0,0 +1,68 @@ +_offer = new Response\Offer(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_offer = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $name = "Testoffer"; + $amount = 4200; + $currency = "EUR"; + $interval = "1 WEEK"; + $trialPeriodDays = 0; + $active = 3; + $inactive = 0; + + $this->_offer->setName($name)-> + setAmount($amount)-> + setCurrency($currency)-> + setInterval($interval)-> + setTrialPeriodDays($trialPeriodDays)-> + setSubscriptionCount($active, $inactive); + + $this->assertEquals($this->_offer->getName(), $name); + $this->assertEquals($this->_offer->getAmount(), $amount); + $this->assertEquals($this->_offer->getCurrency(), $currency); + $this->assertEquals($this->_offer->getInterval(), $interval); + $this->assertEquals($this->_offer->getTrialPeriodDays(), $trialPeriodDays); + $this->assertEquals($this->_offer->getSubscriptionCount(), array('active' => $active, 'inactive' => $inactive)); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Response/PaymentTest.php b/lib/api/tests/unit/Paymill/Models/Response/PaymentTest.php new file mode 100644 index 0000000..a86d831 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Response/PaymentTest.php @@ -0,0 +1,90 @@ +_payment = new Response\Payment(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_payment = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $type = "Test"; + $client = "Test"; + $cardType = "Test"; + $country = "Test"; + $expireMonth = 1; + $expireYear = 2; + $cardHolder = "Test"; + $lastFour = "Test"; + $code = "Test"; + $account = "Test"; + $holder = "Test"; + $iban = "Test"; + $bic = "Test"; + + + $this->_payment->setType($type) + ->setClient($client) + ->setCardType($cardType) + ->setCountry($country) + ->setExpireMonth($expireMonth) + ->setExpireYear($expireYear) + ->setCardHolder($cardHolder) + ->setLastFour($lastFour) + ->setCode($code) + ->setAccount($account) + ->setHolder($holder) + ->setIban($iban) + ->setBic($bic); + + + $this->assertEquals($this->_payment->getType(),$type); + $this->assertEquals($this->_payment->getClient(),$client); + $this->assertEquals($this->_payment->getCardType(),$cardType); + $this->assertEquals($this->_payment->getCountry(),$country); + $this->assertEquals($this->_payment->getExpireMonth(),$expireMonth); + $this->assertEquals($this->_payment->getExpireYear(),$expireYear); + $this->assertEquals($this->_payment->getCardHolder(),$cardHolder); + $this->assertEquals($this->_payment->getLastFour(),$lastFour); + $this->assertEquals($this->_payment->getCode(),$code); + $this->assertEquals($this->_payment->getAccount(),$account); + $this->assertEquals($this->_payment->getHolder(),$holder); + $this->assertEquals($this->_payment->getBic(),$bic); + $this->assertEquals($this->_payment->getIban(),$iban); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Response/PreauthorizationTest.php b/lib/api/tests/unit/Paymill/Models/Response/PreauthorizationTest.php new file mode 100644 index 0000000..b8dc745 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Response/PreauthorizationTest.php @@ -0,0 +1,67 @@ +_preauthorization = new Response\Preauthorization(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_preauthorization = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $amount = "4200"; + $currency = "EUR"; + $status = "closed"; + $livemode = false; + $payment = new Response\Payment(); + $client = new Response\Client(); + + $this->_preauthorization->setAmount($amount) + ->setCurrency($currency) + ->setStatus($status) + ->setLivemode($livemode) + ->setPayment($payment) + ->setClient($client); + + $this->assertEquals($this->_preauthorization->getAmount(), $amount); + $this->assertEquals($this->_preauthorization->getCurrency(), $currency); + $this->assertEquals($this->_preauthorization->getStatus(), $status); + $this->assertEquals($this->_preauthorization->getLivemode(), $livemode); + $this->assertEquals($this->_preauthorization->getPayment(), $payment); + $this->assertEquals($this->_preauthorization->getClient(), $client); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Response/RefundTest.php b/lib/api/tests/unit/Paymill/Models/Response/RefundTest.php new file mode 100644 index 0000000..09b4d26 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Response/RefundTest.php @@ -0,0 +1,67 @@ +_refund = new Response\Refund(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_refund = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $amount = "010"; + $status = "closed"; + $description = "Test Description"; + $livemode = false; + $responseCode = 20000; + $transaction = new Response\Transaction(); + + $this->_refund->setAmount($amount) + ->setStatus($status) + ->setDescription($description) + ->setLivemode($livemode) + ->setResponseCode($responseCode) + ->setTransaction($transaction); + + $this->assertEquals($this->_refund->getAmount(), $amount); + $this->assertEquals($this->_refund->getStatus(), $status); + $this->assertEquals($this->_refund->getDescription(), $description); + $this->assertEquals($this->_refund->getLivemode(), $livemode); + $this->assertEquals($this->_refund->getResponseCode(), $responseCode); + $this->assertEquals($this->_refund->getTransaction(), $transaction); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Response/SubscriptionTest.php b/lib/api/tests/unit/Paymill/Models/Response/SubscriptionTest.php new file mode 100644 index 0000000..d696ba2 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Response/SubscriptionTest.php @@ -0,0 +1,76 @@ +_subscription = new Response\Subscription(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_subscription = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $offer = new Response\Offer(); + $liveMode = false; + $cancelAtPeriodEnd = false; + $trialStart = null; + $trialEnd = null; + $nextCaptureAt = 1369563095; + $canceledAt = null; + $payment = new Response\Payment(); + $client = new Response\Client(); + + $this->_subscription->setOffer($offer) + ->setLivemode($liveMode) + ->setCancelAtPeriodEnd($cancelAtPeriodEnd) + ->setTrialStart($trialStart) + ->setTrialEnd($trialEnd) + ->setNextCaptureAt($nextCaptureAt) + ->setCanceledAt($canceledAt) + ->setClient($client) + ->setPayment($payment); + + $this->assertEquals($this->_subscription->getOffer(), $offer); + $this->assertEquals($this->_subscription->getLivemode(), $liveMode); + $this->assertEquals($this->_subscription->getCancelAtPeriodEnd(), $cancelAtPeriodEnd); + $this->assertEquals($this->_subscription->getTrialStart(), $trialStart); + $this->assertEquals($this->_subscription->getTrialEnd(), $trialEnd); + $this->assertEquals($this->_subscription->getNextCaptureAt(), $nextCaptureAt); + $this->assertEquals($this->_subscription->getCanceledAt(), $canceledAt); + $this->assertEquals($this->_subscription->getClient(), $client); + $this->assertEquals($this->_subscription->getPayment(), $payment); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Response/TransactionTest.php b/lib/api/tests/unit/Paymill/Models/Response/TransactionTest.php new file mode 100644 index 0000000..9257003 --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Response/TransactionTest.php @@ -0,0 +1,91 @@ +_transaction = new Response\Transaction(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_transaction = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $amount = "4200"; + $originAmount = 4200; + $status = "closed"; + $description = "Test Desc"; + $livemode = false; + $refunds = null; + $currency = "EUR"; + $responseCode = 200000; + $shortId = "This is a short string?!"; + $invoices = array(); + $payment = new Response\Payment(); + $client = new Response\Client(); + $preAuth = new Response\Preauthorization(); + $fees = array(); + + $this->_transaction->setAmount($amount) + ->setOriginAmount($originAmount) + ->setStatus($status) + ->setDescription($description) + ->setLivemode($livemode) + ->setRefunds($refunds) + ->setCurrency($currency) + ->setResponseCode($responseCode) + ->setShortId($shortId) + ->setInvoices($invoices) + ->setPayment($payment) + ->setClient($client) + ->setPreauthorization($preAuth) + ->setFees($fees); + + $this->assertEquals($this->_transaction->getAmount(), $amount); + $this->assertEquals($this->_transaction->getOriginAmount(), $originAmount); + $this->assertEquals($this->_transaction->getStatus(), $status); + $this->assertEquals($this->_transaction->getDescription(), $description); + $this->assertEquals($this->_transaction->getLivemode(), $livemode); + $this->assertEquals($this->_transaction->getRefunds(), $refunds); + $this->assertEquals($this->_transaction->getCurrency(), $currency); + $this->assertEquals($this->_transaction->getResponseCode(), $responseCode); + $this->assertEquals($this->_transaction->getShortId(), $shortId); + $this->assertEquals($this->_transaction->getInvoices(), $invoices); + $this->assertEquals($this->_transaction->getPayment(), $payment); + $this->assertEquals($this->_transaction->getClient(), $client); + $this->assertEquals($this->_transaction->getPreauthorization(), $preAuth); + $this->assertEquals($this->_transaction->getFees(), $fees); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Models/Response/WebhookTest.php b/lib/api/tests/unit/Paymill/Models/Response/WebhookTest.php new file mode 100644 index 0000000..41b560a --- /dev/null +++ b/lib/api/tests/unit/Paymill/Models/Response/WebhookTest.php @@ -0,0 +1,65 @@ +_webhook = new Response\Webhook(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_webhook = null; + parent::tearDown(); + } + + //Testmethods + /** + * Tests the getters and setters of the model + * @test + */ + public function setGetTest() + { + $url = "www.test.ing"; + $email = "test@test.ing"; + $livemode = false; + $eventTypes = array( + "transaction.succeeded", + "transaction.failed" + ); + + + $this->_webhook->setUrl($url) + ->setEmail($email) + ->setLivemode($livemode) + ->setEventTypes($eventTypes); + + $this->assertEquals($this->_webhook->getUrl(), $url); + $this->assertEquals($this->_webhook->getEmail(), $email); + $this->assertEquals($this->_webhook->getLivemode(), $livemode); + $this->assertEquals($this->_webhook->getEventTypes(), $eventTypes); + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Services/RequestTest.php b/lib/api/tests/unit/Paymill/Services/RequestTest.php new file mode 100644 index 0000000..2f71cec --- /dev/null +++ b/lib/api/tests/unit/Paymill/Services/RequestTest.php @@ -0,0 +1,502 @@ +_request = new RequestService(); + $this->_client = new Request\Client(); + $this->_curlObjectMock = $this->getMock('Paymill\API\Curl', array('requestApi'), array("TestToken")); + $this->_request->setSource("UNITTEST"); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_request = null; + $this->_client = null; + parent::tearDown(); + } + + /** + * Test the setter for the connection class + * @test + */ + public function setConnectionClassTest() + { + $connector = new Curl("This connector will never be used."); + $request = $this->_request->setConnectionClass($connector); + $this->assertEquals($request, $this->_request); + } + + /** + * Test the setter for the connection class + * @test + */ + public function setConnectionClassWithinConstructorTest() + { + $this->_request = new RequestService("TestToken"); + $this->assertInstanceOf('Paymill\Request', $this->_request); + } + + /** + * Test the setter for the connection class + * @test + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage The connenction class is missing! + */ + public function missingConnectionClassTest() + { + $this->_request = new RequestService(); + $this->_request->getOne($this->_client); + } + + /** + * Tests the create request method + * @test + */ + public function createTest() + { + $inputArray = array( + 'email' => "max.mustermann@example.com", + 'description' => "Lovely Client" + ); + $inputModel = $this->_client->setEmail($inputArray['email'])->setDescription($inputArray['description']); + $outputArray = array(); + $outputArray['header']['status'] = 200; + $outputArray['body']['data'] = array( + "id" => "client_88a388d9dd48f86c3136", + "email" => "lovely-client@example.com", + "description" => "Lovely Client", + "created_at" => 1342438695, + "updated_at" => 1342438695, + "payment" => array( + "id" => "pay_3af44644dd6d25c820a8", + "type" => "creditcard", + "client" => "client_88a388d9dd48f86c3136", + "card_type" => "visa", + "country" => null, + "expire_month" => 10, + "expire_year" => 2013, + "card_holder" => null, + "last4" => "1111", + "created_at" => 1349942085, + "updated_at" => 1349942085, + "app_id" => null + ), + "subscription" => null, + "app_id" => null + ); + $paymentModel = new Response\Payment(); + $paymentModel->setId($outputArray['body']['data']['payment']['id']) + ->setType($outputArray['body']['data']['payment']['type']) + ->setClient($outputArray['body']['data']['payment']['client']) + ->setCardType($outputArray['body']['data']['payment']['card_type']) + ->setCountry($outputArray['body']['data']['payment']['country']) + ->setExpireMonth($outputArray['body']['data']['payment']['expire_month']) + ->setExpireYear($outputArray['body']['data']['payment']['expire_year']) + ->setCardHolder($outputArray['body']['data']['payment']['card_holder']) + ->setLastFour($outputArray['body']['data']['payment']['last4']) + ->setCreatedAt($outputArray['body']['data']['payment']['created_at']) + ->setUpdatedAt($outputArray['body']['data']['payment']['updated_at']) + ->setAppId($outputArray['body']['data']['payment']['app_id']); + $outputModel = new Response\Client(); + $outputModel->setId($outputArray['body']['data']['id']) + ->setEmail($outputArray['body']['data']['email']) + ->setDescription($outputArray['body']['data']['description']) + ->setCreatedAt($outputArray['body']['data']['created_at']) + ->setUpdatedAt($outputArray['body']['data']['updated_at']) + ->setPayment($paymentModel) + ->setSubscription($outputArray['body']['data']['subscription']) + ->setAppId($outputArray['body']['data']['app_id']); + + $this->_getCurlMock( + $this->_client->getServiceResource() . $this->_client->getId(), $inputArray, "POST", $outputArray + ); + + $this->assertEquals($outputModel, $this->_request->create($inputModel)); + return $this->_client; + } + + /** + * Tests the update request method + * @test + */ + public function updateTest() + { + $outputArray = array(); + $outputArray['header']['status'] = 200; + $outputArray['body']['data'] = array( + "id" => "client_88a388d9dd48f86c3136", + "email" => null, + "description" => "Lovely Client", + "created_at" => 1342438695, + "updated_at" => 1342438695, + "payment" => array( + "id" => "pay_3af44644dd6d25c820a8", + "type" => "creditcard", + "client" => "client_88a388d9dd48f86c3136", + "card_type" => "visa", + "country" => null, + "expire_month" => 10, + "expire_year" => 2013, + "card_holder" => null, + "last4" => "1111", + "created_at" => 1349942085, + "updated_at" => 1349942085, + "app_id" => null + ), + "subscription" => null, + "app_id" => null + ); + $paymentModel = new Response\Payment(); + $paymentModel->setId($outputArray['body']['data']['payment']['id']) + ->setType($outputArray['body']['data']['payment']['type']) + ->setClient($outputArray['body']['data']['payment']['client']) + ->setCardType($outputArray['body']['data']['payment']['card_type']) + ->setCountry($outputArray['body']['data']['payment']['country']) + ->setExpireMonth($outputArray['body']['data']['payment']['expire_month']) + ->setExpireYear($outputArray['body']['data']['payment']['expire_year']) + ->setCardHolder($outputArray['body']['data']['payment']['card_holder']) + ->setLastFour($outputArray['body']['data']['payment']['last4']) + ->setCreatedAt($outputArray['body']['data']['payment']['created_at']) + ->setUpdatedAt($outputArray['body']['data']['payment']['updated_at']) + ->setAppId($outputArray['body']['data']['payment']['app_id']); + $outputModel = new Response\Client(); + $outputModel->setId($outputArray['body']['data']['id']) + ->setEmail($outputArray['body']['data']['email']) + ->setDescription($outputArray['body']['data']['description']) + ->setCreatedAt($outputArray['body']['data']['created_at']) + ->setUpdatedAt($outputArray['body']['data']['updated_at']) + ->setPayment($paymentModel) + ->setSubscription($outputArray['body']['data']['subscription']) + ->setAppId($outputArray['body']['data']['app_id']); + + $this->_getCurlMock( + $this->_client->getServiceResource() . $this->_client->getId(), $this->_client->parameterize("update"), "PUT", $outputArray + ); + + $this->_client->setEmail(null); + + $this->_client = $this->_request->update($this->_client); + $this->assertEquals($outputModel, $this->_client); + return $this->_client; + } + + /** + * Tests the delete request method + * @test + * @depends updateTest + */ + public function deleteTest($client) + { + + $inputModel = new Request\Client(); + $inputModel->setId($client->getId()); + $outputArray = array(); + $outputArray['header']['status'] = 200; + $outputArray['body']['data'] = array( + "id" => "client_88a388d9dd48f86c3136", + "email" => null, + "description" => "Lovely Client", + "created_at" => 1342438695, + "updated_at" => 1342438695, + "payment" => array( + "id" => "pay_3af44644dd6d25c820a8", + "type" => "creditcard", + "client" => "client_88a388d9dd48f86c3136", + "card_type" => "visa", + "country" => null, + "expire_month" => 10, + "expire_year" => 2013, + "card_holder" => null, + "last4" => "1111", + "created_at" => 1349942085, + "updated_at" => 1349942085, + "app_id" => null + ), + "subscription" => null, + "app_id" => null + ); + + $paymentModel = new Response\Payment(); + $paymentModel->setId($outputArray['body']['data']['payment']['id']) + ->setType($outputArray['body']['data']['payment']['type']) + ->setClient($outputArray['body']['data']['payment']['client']) + ->setCardType($outputArray['body']['data']['payment']['card_type']) + ->setCountry($outputArray['body']['data']['payment']['country']) + ->setExpireMonth($outputArray['body']['data']['payment']['expire_month']) + ->setExpireYear($outputArray['body']['data']['payment']['expire_year']) + ->setCardHolder($outputArray['body']['data']['payment']['card_holder']) + ->setLastFour($outputArray['body']['data']['payment']['last4']) + ->setCreatedAt($outputArray['body']['data']['payment']['created_at']) + ->setUpdatedAt($outputArray['body']['data']['payment']['updated_at']) + ->setAppId($outputArray['body']['data']['payment']['app_id']); + $outputModel = new Response\Client(); + $outputModel->setId($outputArray['body']['data']['id']) + ->setEmail($outputArray['body']['data']['email']) + ->setDescription($outputArray['body']['data']['description']) + ->setCreatedAt($outputArray['body']['data']['created_at']) + ->setUpdatedAt($outputArray['body']['data']['updated_at']) + ->setPayment($paymentModel) + ->setSubscription($outputArray['body']['data']['subscription']) + ->setAppId($outputArray['body']['data']['app_id']); + + $this->_getCurlMock( + $this->_client->getServiceResource() . $this->_client->getId(), $this->_client->parameterize("delete"), "DELETE", $outputArray + ); + + $this->_client = $this->_request->delete($inputModel); + $this->assertEquals($outputModel, $this->_client); + return $this->_client; + } + + /** + * Tests the getAll request method + * @test + */ + public function getAllTest() + { + $outputArray['header']['status'] = 200; + $outputArray['body']['data'] = null; + $this->_getCurlMock( + $this->_client->getServiceResource() . $this->_client->getId(), $this->_client->parameterize("getAll"), "GET", $outputArray + ); + $result = $this->_request->getAll($this->_client); + $this->assertEquals($result, null); + } + + /** + * Tests the getOne request method + * @test + * @depends createTest + */ + public function getOneTest($client) + { + + $outputArray = array(); + $outputArray['header']['status'] = 200; + $outputArray['body']['data'] = array( + "id" => "client_88a388d9dd48f86c3136", + "email" => "lovely-client@example.com", + "description" => "Lovely Client", + "created_at" => 1342438695, + "updated_at" => 1342438695, + "payment" => array( + "id" => "pay_3af44644dd6d25c820a8", + "type" => "creditcard", + "client" => "client_88a388d9dd48f86c3136", + "card_type" => "visa", + "country" => null, + "expire_month" => 10, + "expire_year" => 2013, + "card_holder" => null, + "last4" => "1111", + "created_at" => 1349942085, + "updated_at" => 1349942085, + "app_id" => null + ), + "subscription" => null, + "app_id" => null + ); + + $paymentModel = new Response\Payment(); + $paymentModel->setId($outputArray['body']['data']['payment']['id']) + ->setType($outputArray['body']['data']['payment']['type']) + ->setClient($outputArray['body']['data']['payment']['client']) + ->setCardType($outputArray['body']['data']['payment']['card_type']) + ->setCountry($outputArray['body']['data']['payment']['country']) + ->setExpireMonth($outputArray['body']['data']['payment']['expire_month']) + ->setExpireYear($outputArray['body']['data']['payment']['expire_year']) + ->setCardHolder($outputArray['body']['data']['payment']['card_holder']) + ->setLastFour($outputArray['body']['data']['payment']['last4']) + ->setCreatedAt($outputArray['body']['data']['payment']['created_at']) + ->setUpdatedAt($outputArray['body']['data']['payment']['updated_at']) + ->setAppId($outputArray['body']['data']['payment']['app_id']); + $outputModel = new Response\Client(); + $outputModel->setId($outputArray['body']['data']['id']) + ->setEmail($outputArray['body']['data']['email']) + ->setDescription($outputArray['body']['data']['description']) + ->setCreatedAt($outputArray['body']['data']['created_at']) + ->setUpdatedAt($outputArray['body']['data']['updated_at']) + ->setPayment($paymentModel) + ->setSubscription($outputArray['body']['data']['subscription']) + ->setAppId($outputArray['body']['data']['app_id']); + + $this->_getCurlMock( + $client->getServiceResource() . $client->getId(), $client->parameterize("getOne"), "GET", $outputArray + ); + + $this->_client = $this->_request->getOne($client); + $this->assertEquals($outputModel, $this->_client); + return $this->_request; + } + + /** + * Tests the exception trigger in the create request method + * @test + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Undefined Error. This should not happen! + */ + public function createExceptionTest() + { + $outputArray = array(); + $outputArray['header']['status'] = 500; + $this->_getCurlMock( + $this->_client->getServiceResource() . $this->_client->getId(), $this->_client->parameterize("create"), "POST", $outputArray + ); + + $this->_client = $this->_request->create($this->_client); + } + + /** + * Tests the exception trigger in the update request method + * @test + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Undefined Error. This should not happen! + */ + public function updateExceptionTest() + { + $outputArray = array(); + $outputArray['header']['status'] = 500; + $this->_getCurlMock( + $this->_client->getServiceResource() . $this->_client->getId(), $this->_client->parameterize("update"), "PUT", $outputArray + ); + + $this->_client = $this->_request->update($this->_client); + } + + /** + * Tests the exception trigger in the delete request method + * @test + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Undefined Error. This should not happen! + */ + public function deleteExceptionTest() + { + $outputArray = array(); + $outputArray['header']['status'] = 500; + $this->_getCurlMock( + $this->_client->getServiceResource() . $this->_client->getId(), $this->_client->parameterize("delete"), "DELETE", $outputArray + ); + + $this->_client = $this->_request->delete($this->_client); + } + + /** + * Tests the exception trigger in the getAll request method + * @test + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Undefined Error. This should not happen! + */ + public function getAllExceptionTest() + { + $outputArray = array(); + $outputArray['header']['status'] = 666; + $this->_getCurlMock( + $this->_client->getServiceResource() . $this->_client->getId(), $this->_client->parameterize("getAll"), "GET", $outputArray + ); + + $this->_client = $this->_request->getAll($this->_client); + } + + + /** + * Tests the exception trigger in the getOne request method + * @test + * @expectedException \Paymill\Services\PaymillException + * @expectedExceptionMessage Undefined Error. This should not happen! + */ + public function getOneExceptionTest() + { + $outputArray = array(); + $outputArray['header']['status'] = 500; + $this->_getCurlMock( + $this->_client->getServiceResource() . $this->_client->getId(), $this->_client->parameterize("getOne"), "GET", $outputArray + ); + + $this->_client = $this->_request->getOne($this->_client); + } + + /** + * Tests the getter for the last response array + * @test + * @param Request $request + * @depends getOneTest + */ + public function getLastResponseTest($request) + { + $result = $request->getLastResponse(); + $this->assertInternalType("array", $result); + } + + /** + * Tests the getter for the last response array + * @test + * @param Request $request + * @depends getOneTest + */ + public function getLastRequestTest($request) + { + $result = $request->getLastRequest(); + $this->assertInternalType("array", $result); + $this->assertArrayHasKey("count", $result); + $this->assertArrayHasKey("offset", $result); + } + + /** + * Returns a mocked Curl Object + * @param string $action Api Action + * @param array $params Param Array for the action call + * @param string $method httpMethod + * @param array $response Array returned by the function + * @return array Response Array + */ + private function _getCurlMock($action, $params, $method, $response) + { + $this->_curlObjectMock->expects($this->any()) + ->method('requestApi') + ->with( + $this->stringContains($action), $this->equalTo($params), $this->matches($method) + ) + ->will($this->returnValue($response)); + $this->_request->setConnectionClass($this->_curlObjectMock); + return $this->_curlObjectMock; + } + +} \ No newline at end of file diff --git a/lib/api/tests/unit/Paymill/Services/ResponseHandlerTest.php b/lib/api/tests/unit/Paymill/Services/ResponseHandlerTest.php new file mode 100644 index 0000000..c112a4e --- /dev/null +++ b/lib/api/tests/unit/Paymill/Services/ResponseHandlerTest.php @@ -0,0 +1,684 @@ + "General undefined response.", + 10002 => "Still waiting on something.", + 20000 => "General success response.", + 40000 => "General problem with data.", + 40001 => "General problem with payment data.", + 40100 => "Problem with credit card data.", + 40101 => "Problem with cvv.", + 40102 => "Card expired or not yet valid.", + 40103 => "Limit exceeded.", + 40104 => "Card invalid.", + 40105 => "Expiry date not valid.", + 40106 => "Credit card brand required.", + 40200 => "Problem with bank account data.", + 40201 => "Bank account data combination mismatch.", + 40202 => "User authentication failed.", + 40300 => "Problem with 3d secure data.", + 40301 => "Currency / amount mismatch", + 40400 => "Problem with input data.", + 40401 => "Amount too low or zero.", + 40402 => "Usage field too long.", + 40403 => "Currency not allowed.", + 50000 => "General problem with backend.", + 50001 => "Country blacklisted.", + 50100 => "Technical error with credit card.", + 50101 => "Error limit exceeded.", + 50102 => "Card declined by authorization system.", + 50103 => "Manipulation or stolen card.", + 50104 => "Card restricted.", + 50105 => "Invalid card configuration data.", + 50200 => "Technical error with bank account.", + 50201 => "Card blacklisted.", + 50300 => "Technical error with 3D secure.", + 50400 => "Decline because of risk issues.", + 50500 => "General timeout.", + 50501 => "Timeout on side of the acquirer.", + 50502 => "Risk management transaction timeout.", + 50600 => "Duplicate transaction.", + ); + + /** + * + * @var \Paymill\Services\ResponseHandler + */ + private $_responseHandler; + + /** + * Prepares the environment before running a test. + */ + protected function setUp() + { + parent::setUp(); + $this->_responseHandler = new ResponseHandler(); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->_responseHandler = null; + parent::tearDown(); + } + + /** + * Tests the convertResponseModel method with the client model as outcome + * @test + */ + public function clientTest() + { + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "client_88a388d9dd48f86c3136", + "email" => "lovely-client@example.com", + "description" => null, + "created_at" => 1340199740, + "updated_at" => 1340199760, + "payment" => array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + ), + "subscription" => null, + "app_id" => null + ); + $subject = $this->_responseHandler->convertResponse($response, "clients/"); + $this->assertInstanceOf("\Paymill\Models\Response\Client", $subject, var_export($subject, true)); + } + + /** + * Tests the convertResponseModel method with the client model as outcome using a client with multiple payment objects + * @test + */ + public function clientMultiPaymentTest() + { + $response = array(); + $response['header']['status'] = 200; + $response['body']['data'] = array( + 'id' => "client_018dcaf0d8d03dde3ff6", + 'email' => "Some@Testemail.de", + 'description' => "This is a Testuser.123", + 'created_at' => 1378472311, + 'updated_at' => 1378472311, + 'app_id' => null, + 'payment' => array( + array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + ), array + ( + 'id' => "pay_8ff8fb0e864ea55b8b2eb876", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472406, + 'updated_at' => 1378472407, + 'app_id' => null + ) + ), + 'subscription' => null); + + $subject = $this->_responseHandler->convertResponse($response, "clients/"); + $this->assertInstanceOf("\Paymill\Models\Response\Client", $subject, var_export($subject, true)); + $this->assertInternalType("array", $subject->getPayment(), var_export($subject, true)); + } + + /** + * Tests the convertResponseModel method with the payment model as outcome + * @test + */ + public function paymentCCTest() + { + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "pay_3af44644dd6d25c820a8", + "type" => "creditcard", + "client" => null, + "card_type" => "visa", + "country" => null, + "expire_month" => 10, + "expire_year" => 2013, + "card_holder" => null, + "last4" => "1111", + "created_at" => 1349942085, + "updated_at" => 1349942085, + "app_id" => null + ); + $subject = $this->_responseHandler->convertResponse($response, "payments/"); + $this->assertInstanceOf("\Paymill\Models\Response\Payment", $subject, var_export($subject, true)); + } + + /** + * Tests the convertResponseModel method with the payment model as outcome + * @test + */ + public function paymentSEPATest() + { + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "pay_3af44644dd6d25c820a8", + "type" => "debit", + "client" => null, + "code" => "70090100", + "account" => "******7890", + "iban" => "DE0870090100******7890", + "bic" => "DEUTDEDB110", + "holder" => "test", + "created_at" => 1349942085, + "updated_at" => 1349942085, + "app_id" => null + ); + $subject = $this->_responseHandler->convertResponse($response, "payments/"); + $this->assertInstanceOf("\Paymill\Models\Response\Payment", $subject, var_export($subject, true)); + $this->assertEquals("DE0870090100******7890", $subject->getIban()); + $this->assertEquals("DEUTDEDB110", $subject->getBic()); + } + + /** + * Tests the convertResponseModel method with the transaction model as outcome + * @test + */ + public function transactionTest() + { + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "tran_54645bcb98ba7acfe204", + "amount" => "4200", + "origin_amount" => 4200, + "status" => "closed", + "description" => null, + "livemode" => false, + "refunds" => null, + "currency" => "EUR", + "created_at" => 1349946151, + "updated_at" => 1349946151, + "response_code" => 20000, + "short_id" => '0000.1212.3434', + "invoices" => array(), + "payment" => array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + ), + "client" => array( + "id" => "client_88a388d9dd48f86c3136", + "email" => "lovely-client@example.com", + "description" => null, + "created_at" => 1340199740, + "updated_at" => 1340199760, + "subscription" => null, + 'app_id' => null, + "payment" => array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + )), + "preauthorization" => null, + "fees" => array(), + "app_id" => null + ); + $subject = $this->_responseHandler->convertResponse($response, "transactions/"); + $this->assertInstanceOf("\Paymill\Models\Response\Transaction", $subject, var_export($subject, true)); + } + + /** + * Tests the convertResponseModel method with the preauthorization model as outcome + * @test + */ + public function preauthorizationTest() + { + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "tran_54645bcb98ba7acfe204", + "amount" => "4200", + "origin_amount" => 4200, + "status" => "closed", + "description" => null, + "livemode" => false, + "refunds" => null, + "currency" => "EUR", + "created_at" => 1349946151, + "updated_at" => 1349946151, + "response_code" => 20000, + "short_id" => '0000.1212.3434', + "invoices" => array(), + "payment" => array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + ), + "client" => array( + "id" => "client_88a388d9dd48f86c3136", + "email" => "lovely-client@example.com", + "description" => null, + "created_at" => 1340199740, + "updated_at" => 1340199760, + "subscription" => null, + 'app_id' => null, + "payment" => array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + )), + "preauthorization" => array( + "id" => "preauth_0b771c503680c341548e", + "amount" => "4200", + "currency" => "EUR", + "status" => "closed", + "livemode" => false, + "created_at" => 1349950324, + "updated_at" => 1349950324, + "payment" => array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + ), + "client" => array( + "id" => "client_88a388d9dd48f86c3136", + "email" => "lovely-client@example.com", + "description" => null, + "created_at" => 1340199740, + "updated_at" => 1340199760, + "subscription" => null, + 'app_id' => null, + "payment" => array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + )), + "app_id" => null + ), + "fees" => array(), + "app_id" => null + ); + + $subject = $this->_responseHandler->convertResponse($response, "preauthorizations/"); + $this->assertInstanceOf("\Paymill\Models\Response\Preauthorization", $subject, var_export($subject, true)); + } + + /** + * Tests the convertResponseModel method with the refund model as outcome + * @test + */ + public function refundTest() + { + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "refund_87bc404a95d5ce616049", + "amount" => "042", + "status" => "refunded", + "description" => null, + "livemode" => false, + "created_at" => 1349947042, + "updated_at" => 1349947042, + "response_code" => 20000, + "transaction" => array( + "id" => "tran_54645bcb98ba7acfe204", + "amount" => "4200", + "origin_amount" => 4200, + "status" => "closed", + "description" => null, + "livemode" => false, + "refunds" => null, + "currency" => "EUR", + "created_at" => 1349946151, + "updated_at" => 1349946151, + "response_code" => 20000, + "short_id" => '0000.1212.3434', + "invoices" => array(), + "payment" => array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + ), + "client" => array( + "id" => "client_88a388d9dd48f86c3136", + "email" => "lovely-client@example.com", + "description" => null, + "created_at" => 1340199740, + "updated_at" => 1340199760, + "subscription" => null, + 'app_id' => null, + "payment" => array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + )), + "preauthorization" => null, + "fees" => array(), + "app_id" => null + ), + "app_id" => null + ); + $subject = $this->_responseHandler->convertResponse($response, "refunds/"); + $this->assertInstanceOf("\Paymill\Models\Response\Refund", $subject, var_export($subject, true)); + } + + /** + * Tests the convertResponseModel method with the offer model as outcome + * @test + */ + public function offerTest() + { + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "offer_40237e20a7d5a231d99b", + "name" => "Nerd Special", + "amount" => 4200, + "currency" => "EUR", + "interval" => "1 WEEK", + "trial_period_days" => 0, + "created_at" => 1341935129, + "updated_at" => 1341935129, + "subscription_count" => array( + "active" => "3", + "inactive" => 0 + ), + "app_id" => null + ); + $subject = $this->_responseHandler->convertResponse($response, "offers/"); + $this->assertInstanceOf("\Paymill\Models\Response\Offer", $subject, var_export($subject, true)); + } + + /** + * Tests the convertResponseModel method with the subscription model as outcome + * @test + */ + public function subscriptionTest() + { + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "sub_012db05186ccfe22d86c", + "offer" => array( + "id" => "offer_40237e20a7d5a231d99b", + "name" => "Nerd Special", + "amount" => 4200, + "currency" => "EUR", + "interval" => "1 WEEK", + "trial_period_days" => 0, + "created_at" => 1341935129, + "updated_at" => 1341935129, + "subscription_count" => array( + "active" => "3", + "inactive" => 0 + ), + "app_id" => null + ), + "livemode" => false, + "cancel_at_period_end" => false, + "trial_start" => null, + "trial_end" => null, + "next_capture_at" => 1369563095, + "created_at" => 1341935490, + "updated_at" => 1341935490, + "canceled_at" => null, + "payment" => array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + ), + "client" => array( + "id" => "client_88a388d9dd48f86c3136", + "email" => "lovely-client@example.com", + "description" => null, + "created_at" => 1340199740, + "updated_at" => 1340199760, + "subscription" => null, + 'app_id' => null, + "payment" => array( + 'id' => "pay_be64260ee1b0a368efe597e8", + 'type' => "creditcard", + 'client' => "client_018dcaf0d8d03dde3ff6", + 'card_type' => "visa", + 'country' => null, + 'expire_month' => 12, + 'expire_year' => 2015, + 'card_holder' => null, + 'last4' => 1111, + 'created_at' => 1378472387, + 'updated_at' => 1378472387, + 'app_id' => null + )), + "app_id" => null + ); + $subject = $this->_responseHandler->convertResponse($response, "subscriptions/"); + $this->assertInstanceOf("\Paymill\Models\Response\Subscription", $subject, var_export($subject, true)); + } + + /** + * Tests the convertResponseModel method with the url version of the Webhook model as outcome + * @test + */ + public function urlWebhookTest() + { + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "hook_40237e20a7d5a231d99b", + "url" => "your-webhook-url", + "livemode" => false, + "event_types" => array( + "transaction.succeeded", + "transaction.failed" + ), + "created_at" => 1358982000, + "updated_at" => 1358982000, + "app_id" => null + ); + $subject = $this->_responseHandler->convertResponse($response, "webhooks/"); + $this->assertInstanceOf("\Paymill\Models\Response\Webhook", $subject, var_export($subject, true)); + } + + /** + * Tests the convertResponseModel method with the email version of the Webhook model as outcome + * @test + */ + public function emailWebhookTest() + { + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "hook_40237e20a7d5a231d99b", + "email" => "your-webhook-email", + "livemode" => false, + "event_types" => array( + "transaction.succeeded", + "transaction.failed" + ), + "created_at" => 1358982000, + "updated_at" => 1358982000, + "app_id" => null + ); + $subject = $this->_responseHandler->convertResponse($response, "webhooks/"); + $this->assertInstanceOf("\Paymill\Models\Response\Webhook", $subject, var_export($subject, true)); + } + + /** + * Tests the handling of ResponseCodes + * @test + */ + public function checkResponseCodes() + { + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "tran_54645bcb98ba7acfe204", + "amount" => "4200", + "origin_amount" => 4200, + "status" => "closed", + "description" => null, + "livemode" => false, + "refunds" => null, + "currency" => "EUR", + "created_at" => 1349946151, + "updated_at" => 1349946151, + "short_id" => '0000.1212.3434', + "invoices" => array(), + "payment" => new Models\Response\Payment(), + "client" => new Models\Response\Client(), + "preauthorization" => null, + "fees" => array(), + "app_id" => null + ); + + foreach ($this->_errorCodes as $responseCode => $errorMessage) { + if ($responseCode === 20000) { + continue; + } + $response['body']['data']['response_code'] = $responseCode; + $subject = $this->_responseHandler->convertResponse($response, "transactions/"); + $this->assertInstanceOf("\Paymill\Models\Response\Error", $subject); + $this->assertEquals($responseCode, $subject->getResponseCode(), "ResponseCode:" . $responseCode . "==" . $subject->getResponseCode() . "\n"); + $this->assertEquals($errorMessage, $subject->getErrorMessage(), "ErrorMessage:" . $errorMessage . "==" . $subject->getErrorMessage() . "\n"); + $response['body']['data']['response_code'] = null; + } + } + + /** + * @test + */ + public function ProveConversionToArray(){ + $response = array(); + $response['header']['status'] = 200; + $response['body']['data'] = array( + "id" => "tran_54645bcb98ba7acfe204", + "amount" => "4200", + "origin_amount" => 4200, + "status" => "closed", + "description" => null, + "livemode" => false, + "refunds" => null, + "currency" => "EUR", + "created_at" => 1349946151, + "updated_at" => 1349946151, + "short_id" => '0000.1212.3434', + "invoices" => array(), + "payment" => new Models\Response\Payment(), + "client" => new Models\Response\Client(), + "preauthorization" => null, + "fees" => array(), + "app_id" => null + ); + $responseObject = $this->_responseHandler->arrayToObject($response['body']); + $this->assertInstanceOf('stdClass', $responseObject); + $this->assertEquals($response['body']['data']['id'], $responseObject->data->id); + } + +} \ No newline at end of file diff --git a/lib/benchmark.inc.php b/lib/benchmark.inc.php new file mode 100644 index 0000000..2662751 --- /dev/null +++ b/lib/benchmark.inc.php @@ -0,0 +1,57 @@ +'; + echo 'FeatureUser TimeSystem TimeSum'; + foreach($GLOBALS['paymillBenchmark'] as $feature => $data){ + echo ''.$feature.''.$data['userTime'].''.$data['systemTime'].''.$data['sumTime'].''; + $userSum = $userSum + $data['userTime']; + $systemSum = $systemSum + $data['systemTime']; + $sum = $sum + $data['sumTime']; + } + echo 'Sum'.$userSum.''.$systemSum.''.$sum.''; + echo ''; + + global $wpdb; + echo "
";
+					var_dump($wpdb->queries);
+					echo "
"; + } + }else{ + static $stime; + static $utime; + static $sumtime; + $ru = getrusage(); + $currentUTime = $ru['ru_utime.tv_sec'] + round($ru['ru_utime.tv_usec']/1000000, 4); + $currentSTime = $ru['ru_stime.tv_sec'] + round($ru['ru_stime.tv_usec']/1000000, 4); + $currentSumTime = $currentUTime+$currentSTime; + + if(($stime || $utime) && !$clear){ + $benchmarkU = $currentUTime - $utime; + $benchmarkS = $currentSTime - $stime; + + if(isset($GLOBALS['paymillBenchmark'][$feature]['userTime'])){ + $GLOBALS['paymillBenchmark'][$feature]['userTime'] = $GLOBALS['paymillBenchmark'][$feature]['userTime'] + $benchmarkU; + $GLOBALS['paymillBenchmark'][$feature]['systemTime'] = $GLOBALS['paymillBenchmark'][$feature]['systemTime'] + $benchmarkS; + $GLOBALS['paymillBenchmark'][$feature]['sumTime'] = $GLOBALS['paymillBenchmark'][$feature]['sumTime'] + $benchmarkU+$benchmarkS; + }else{ + $GLOBALS['paymillBenchmark'][$feature]['userTime'] = $benchmarkU; + $GLOBALS['paymillBenchmark'][$feature]['systemTime'] = $benchmarkS; + $GLOBALS['paymillBenchmark'][$feature]['sumTime'] = $benchmarkU+$benchmarkS; + } + } + $utime = $currentUTime; + $stime = $currentSTime; + } + } + } +?> \ No newline at end of file diff --git a/lib/config.inc.php b/lib/config.inc.php index 12473b3..d90abcf 100644 --- a/lib/config.inc.php +++ b/lib/config.inc.php @@ -1,448 +1,492 @@ setting_keys['paymill_general_settings'] = 'paymill_general_settings'; - $this->setting_keys['paymill_pay_button_settings'] = 'paymill_pay_button_settings'; - - foreach($this->setting_keys as $key){ - $this->$key = (array) get_option( $key ); - } - - // Merge with defaults - $this->paymill_general_settings = array_merge( array( - 'api_endpoint' => 'https://api.paymill.com/v2/', - 'currency' => 'EUR' - ), $this->paymill_general_settings ); - - $this->paymill_pay_button_settings = array_merge( array( - 'number_decimal' => '.', - 'number_thousands' => ',', - ), $this->paymill_pay_button_settings ); - - if(isset($this->paymill_general_settings['api_key_private']) && isset($this->paymill_general_settings['api_key_public']) && $this->paymill_general_settings['api_key_private'] != '' && $this->paymill_general_settings['api_key_public'] != '' && $this->paymill_general_settings['api_endpoint'] != ''){ - define('PAYMILL_ACTIVE',true); - }else{ - define('PAYMILL_ACTIVE',false); - } - - add_action( 'admin_init', array( &$this, 'register_general_settings' ) ); - if(defined('PAYMILL_ACTIVE') && PAYMILL_ACTIVE === true){ - add_action( 'admin_init', array( &$this, 'register_pay_button_settings' ) ); - } - add_action( 'admin_menu', array( &$this, 'add_admin_menus' ) ); + // gather source info for security purposes and optimization + $GLOBALS['paymill_source'] = array( + 'wordpress_version' => get_bloginfo('version'), + 'paymill_version' => PAYMILL_VERSION + ); + // The main plugin class, holds everything our plugin does, initialized right after declaration + class paymill_settings{ + // For easier overriding we declared the keys here as well as our tabs array which is populated when registering settings + public $setting_keys = array(); + private $plugin_options_key = 'paymill_options'; + private $plugin_settings_tabs = array(); - // prepare dynamic language strings - __('DAY', 'paymill'); - __('WEEK', 'paymill'); - __('MONTH', 'paymill'); - __('YEAR', 'paymill'); - __('DAYS', 'paymill'); - __('WEEKS', 'paymill'); - __('MONTHS', 'paymill'); - __('YEARS', 'paymill'); - - __('50501','paymill'); - __('50001','paymill'); - __('50201','paymill'); - __('40103','paymill'); - __('50102','paymill'); - __('50103','paymill'); - __('40105','paymill'); - __('40101','paymill'); - __('40100','paymill'); - __('40104','paymill'); - __('40001','paymill'); - __('40102','paymill'); - __('40106','paymill'); - __('40201','paymill'); - __('50300','paymill'); - __('40202','paymill'); - __('50502','paymill'); - __('40301','paymill'); - __('40401','paymill'); - __('40402','paymill'); - __('40403','paymill'); - __('50104','paymill'); - __('50105','paymill'); - __('50600','paymill'); - - __('shipping'); - __('company_name'); - __('forename'); - __('surname'); - __('street'); - __('number'); - __('zip'); - __('city'); - __('email'); - __('phone'); - - // common errors, for translation purposes - __('Token or Payment required', 'paymill'); - __('Subscription already connected', 'paymill'); + // Fired during plugins_loaded (very very early), so don't miss-use this, only actions and filters, current ones speak for themselves. + public function __construct() { + $this->setting_keys['paymill_general_settings'] = 'paymill_general_settings'; + $this->setting_keys['paymill_pay_button_settings'] = 'paymill_pay_button_settings'; + + foreach($this->setting_keys as $key){ + $this->$key = (array) get_option( $key ); + } + // Merge with defaults + $this->paymill_pay_button_settings = array_merge( array( + 'number_decimal' => '.', + 'number_thousands' => ',', + 'currency' => 'EUR' + ), $this->paymill_pay_button_settings ); + + if(isset($this->paymill_general_settings['api_key_private']) && isset($this->paymill_general_settings['api_key_public']) && $this->paymill_general_settings['api_key_private'] != '' && $this->paymill_general_settings['api_key_public'] != ''){ + define('PAYMILL_ACTIVE',true); + }else{ + define('PAYMILL_ACTIVE',false); + } - } - - /* - * Registers the general settings via the Settings API, - * appends the setting to the tabs array of the object. - */ - function register_general_settings() { - $this->plugin_settings_tabs[$this->setting_keys['paymill_general_settings']] = 'General'; - - register_setting( $this->setting_keys['paymill_general_settings'], $this->setting_keys['paymill_general_settings'] ); - add_settings_section( 'section_general', __('General Plugin Settings', 'paymill'), array( &$this, 'section_general_desc' ), $this->setting_keys['paymill_general_settings'] ); - - add_settings_field( 'api_key_private', __('Paymill PRIVATE API key', 'paymill'), array( &$this, 'field_general_option' ), $this->setting_keys['paymill_general_settings'], 'section_general',array('desc' => 'api_key_private', 'option' => 'api_key_private')); - add_settings_field( 'api_key_public', __('Paymill PUBLIC API key', 'paymill'), array( &$this, 'field_general_option' ), $this->setting_keys['paymill_general_settings'], 'section_general',array('desc' => 'api_key_public', 'option' => 'api_key_public')); - add_settings_field( 'api_endpoint', __('Paymill API endpoint URL', 'paymill'), array( &$this, 'field_general_option' ), $this->setting_keys['paymill_general_settings'], 'section_general',array('desc' => 'api_endpoint', 'option' => 'api_endpoint')); - add_settings_field( 'currency', __('Currency', 'paymill'), array( &$this, 'field_general_option' ), $this->setting_keys['paymill_general_settings'], 'section_general',array('desc' => 'currency', 'option' => 'currency')); - add_settings_field( 'payments_display', __('Display Payment Types', 'paymill'), array( &$this, 'field_general_option' ), $this->setting_keys['paymill_general_settings'], 'section_general',array('desc' => 'payments_display', 'option' => 'payments_display')); - } - - /* - * Registers the pay_button settings and appends the - * key to the plugin settings tabs array. - */ - function register_pay_button_settings() { - $this->plugin_settings_tabs[$this->setting_keys['paymill_pay_button_settings']] = 'Pay Button'; - register_setting( $this->setting_keys['paymill_pay_button_settings'], $this->setting_keys['paymill_pay_button_settings'] ); + add_action( 'admin_init', array( &$this, 'paymill_register_general_settings' ) ); + if(defined('PAYMILL_ACTIVE') && PAYMILL_ACTIVE === true){ + add_action( 'admin_init', array( &$this, 'register_pay_button_settings' ) ); + } + add_action( 'admin_menu', array( &$this, 'add_admin_menus' ) ); + + // prepare dynamic language strings + __('DAY', 'paymill'); + __('WEEK', 'paymill'); + __('MONTH', 'paymill'); + __('YEAR', 'paymill'); + __('DAYS', 'paymill'); + __('WEEKS', 'paymill'); + __('MONTHS', 'paymill'); + __('YEARS', 'paymill'); + + __('50501','paymill'); + __('50001','paymill'); + __('50201','paymill'); + __('40103','paymill'); + __('50102','paymill'); + __('50103','paymill'); + __('40105','paymill'); + __('40101','paymill'); + __('40100','paymill'); + __('40104','paymill'); + __('40001','paymill'); + __('40102','paymill'); + __('40106','paymill'); + __('40201','paymill'); + __('50300','paymill'); + __('40202','paymill'); + __('50502','paymill'); + __('40301','paymill'); + __('40401','paymill'); + __('40402','paymill'); + __('40403','paymill'); + __('50104','paymill'); + __('50105','paymill'); + __('50600','paymill'); + + __('Token not Found','paymill'); + + __('shipping'); + __('company_name'); + __('forename'); + __('surname'); + __('street'); + __('number'); + __('zip'); + __('city'); + __('email'); + __('phone'); + + // common errors, for translation purposes + __('Token or Payment required', 'paymill'); + __('Subscription already connected', 'paymill'); + } + // Registers the general settings via the Settings API, appends the setting to the tabs array of the object. + public function paymill_register_general_settings(){ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_register_general_settings'); // benchmark + + // create or update webhooks when API key changes + if(isset($_GET['settings-updated']) && $_GET['settings-updated'] == 'true' && isset($GLOBALS['paymill_settings']->paymill_general_settings['api_key_private']) && strlen($GLOBALS['paymill_settings']->paymill_general_settings['api_key_private']) > 0){ + paymill_install_webhooks(); + } + + $this->plugin_settings_tabs[$this->setting_keys['paymill_general_settings']] = 'General'; + register_setting($this->setting_keys['paymill_general_settings'], $this->setting_keys['paymill_general_settings']); - // common - add_settings_section( 'section_pay_button', false, array( &$this, 'section_pay_button_desc' ), $this->setting_keys['paymill_pay_button_settings'] ); - add_settings_field( 'number_decimal', __('Number Format: Decimal Point', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button',array('desc' => 'number_decimal', 'option' => 'number_decimal')); - add_settings_field( 'number_thousands', __('Number Format: Thousands Seperator', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button',array('desc' => 'number_thousands', 'option' => 'number_thousands')); - add_settings_field( 'email_outgoing', __('Outgoing Email', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button',array('desc' => 'email_outgoing', 'option' => 'email_outgoing')); - add_settings_field( 'email_incoming', __('Incoming Email', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button',array('desc' => 'email_incoming', 'option' => 'email_incoming')); - add_settings_field( 'thankyou_url', __('Thank You URL', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button',array('desc' => 'thankyou_url', 'option' => 'thankyou_url')); - add_settings_field( 'fields_hide', __('Hide Fields', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button',array('desc' => 'fields_hide', 'option' => 'fields_hide')); - add_settings_field( 'no_default_css', __('Do not load default CSS', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button',array('desc' => 'no_default_css', 'option' => 'no_default_css')); + add_settings_section('section_general', __('General Plugin Settings', 'paymill'), array( &$this, 'section_general_desc'), $this->setting_keys['paymill_general_settings'] ); + $settings = array( + 'api_key_private' => __('Paymill PRIVATE API key', 'paymill'), + 'api_key_public' => __('Paymill PUBLIC API key', 'paymill'), + 'payments_display' => __('Display Payment Types', 'paymill'), + 'no_default_css' => __('Do not load default CSS', 'paymill'), + ); + + foreach($settings as $setting => $description){ + add_settings_field($setting, $description, array(&$this, 'print_config_form_fields'), $this->setting_keys['paymill_general_settings'], 'section_general', array('desc' => $setting, 'page' => $this->setting_keys['paymill_general_settings'])); + } + + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_register_general_settings'); // benchmark + } + // Registers the pay_button settings and appends the key to the plugin settings tabs array. + public function register_pay_button_settings(){ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_register_pay_button_settings'); // benchmark + + $this->plugin_settings_tabs[$this->setting_keys['paymill_pay_button_settings']] = 'Pay Button'; + register_setting( $this->setting_keys['paymill_pay_button_settings'], $this->setting_keys['paymill_pay_button_settings'] ); + + // common + add_settings_section('section_pay_button', false, array( &$this, 'section_pay_button_desc' ), $this->setting_keys['paymill_pay_button_settings']); + $settings = array( + 'number_decimal' => __('Number Format: Decimal Point', 'paymill'), + 'number_thousands' => __('Number Format: Thousands Seperator', 'paymill'), + 'currency' => __('Currency', 'paymill'), + 'email_outgoing' => __('Outgoing Email', 'paymill'), + 'email_incoming' => __('Incoming Email', 'paymill'), + 'thankyou_url' => __('Thank You URL', 'paymill'), + 'fields_show' => __('Show Fields', 'paymill'), + ); + + foreach($settings as $setting => $description){ + add_settings_field($setting, $description, array(&$this, 'print_config_form_fields'), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button', array('desc' => $setting, 'page' => $this->setting_keys['paymill_pay_button_settings'])); + } + + // products + add_settings_section('section_pay_button_products', false, array( &$this, 'section_pay_button_products_desc' ), $this->setting_keys['paymill_pay_button_settings']); - // products - add_settings_section( 'section_pay_button_products', false, array( &$this, 'section_pay_button_products_desc' ), $this->setting_keys['paymill_pay_button_settings'] ); - //if (isset($this->paymill_pay_button_settings['products'])) { - if(strlen($this->paymill_pay_button_settings['products'][count($this->paymill_pay_button_settings['products'])]['title']) > 0){ - $products = count($this->paymill_pay_button_settings['products'])+5; - }elseif(!is_array($this->paymill_pay_button_settings['products']) || count($this->paymill_pay_button_settings['products']) < 5){ - $products = 5; + if(isset($this->paymill_pay_button_settings['products'])){ + if(isset($this->paymill_pay_button_settings['products'][count($this->paymill_pay_button_settings['products'])]['products_title']) && strlen($this->paymill_pay_button_settings['products'][count($this->paymill_pay_button_settings['products'])]['products_title']) > 0){ + $products = count($this->paymill_pay_button_settings['products'])+5; + }else{ + $products = count($this->paymill_pay_button_settings['products']); + } }else{ - $products = count($this->paymill_pay_button_settings['products']); + $products = 5; } + $settings = array( + 'products_title' => __('Product', 'paymill'), + 'products_desc' => __('Description', 'paymill'), + 'products_quantityhide' => __('Hide Quantity', 'paymill'), + 'products_delivery' => __('Delivery Time', 'paymill'), + 'products_vat' => __('VAT', 'paymill'), + 'products_offer' => __('Subscription Offer', 'paymill'), + 'products_price' => __('Price', 'paymill'), + ); + for($i = 1; $i <= $products; $i++){ - add_settings_field( 'products_title_'.$i, __('Product', 'paymill').' #'.$i, array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_products',array('desc' => 'products_title', 'option' => 'products', 'id' => $i, 'field' => 'title')); - add_settings_field( 'products_desc_'.$i, __('Description', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_products',array('desc' => 'products_desc', 'option' => 'products', 'id' => $i, 'field' => 'desc')); - add_settings_field( 'products_vat_'.$i, __('VAT', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_products',array('desc' => 'products_vat', 'option' => 'products', 'id' => $i, 'field' => 'vat')); - - add_settings_field( 'products_offer_'.$i, __('Subscription Offer', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_products',array('desc' => 'products_offer', 'option' => 'products', 'id' => $i, 'field' => 'offer')); - - add_settings_field( 'products_price_'.$i, __('Price', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_products',array('desc' => 'products_price', 'option' => 'products', 'id' => $i, 'field' => 'price')); - add_settings_field( 'products_quantityhide_'.$i, __('Hide Quantity', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_products',array('desc' => 'products_quantityhide', 'option' => 'products', 'id' => $i, 'field' => 'quantityhide')); - //add_settings_field( 'products_freeamount_'.$i, __('Free Amount', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_products',array('desc' => 'products_freeamount', 'option' => 'products', 'id' => $i, 'field' => 'freeamount')); - add_settings_field( 'products_delivery_'.$i, __('Delivery Time', 'paymill'), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_products',array('desc' => 'products_delivery', 'option' => 'products', 'id' => $i, 'field' => 'delivery')); + foreach($settings as $setting => $description){ + add_settings_field($setting.'_'.$i, $description, array(&$this, 'print_config_form_fields'), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_products', array('desc' => $setting, 'id' => $i, 'group' => 'products', 'page' => $this->setting_keys['paymill_pay_button_settings'])); + } } - //} + // shipping + add_settings_section( 'section_pay_button_shipping', false, array( &$this, 'section_pay_button_shipping_desc' ), $this->setting_keys['paymill_pay_button_settings'] ); - // shipping - add_settings_section( 'section_pay_button_shipping', false, array( &$this, 'section_pay_button_shipping_desc' ), $this->setting_keys['paymill_pay_button_settings'] ); - //if(isset($this->paymill_pay_button_settings['flat_shipping'])) { - if(strlen($this->paymill_pay_button_settings['flat_shipping'][count($this->paymill_pay_button_settings['flat_shipping'])]['country']) > 0){ - $countries = count($this->paymill_pay_button_settings['flat_shipping'])+5; - }elseif(count($this->paymill_pay_button_settings['flat_shipping']) < 5){ - $countries = 5; + //var_dump($this->paymill_pay_button_settings['flat_shipping']); + if(isset($this->paymill_pay_button_settings['flat_shipping'])){ + if(isset($this->paymill_pay_button_settings['flat_shipping'][count($this->paymill_pay_button_settings['flat_shipping'])]['flat_shipping_country']) && strlen($this->paymill_pay_button_settings['flat_shipping'][count($this->paymill_pay_button_settings['flat_shipping'])]['flat_shipping_country']) > 0){ + $shipping = count($this->paymill_pay_button_settings['flat_shipping'])+5; + }else{ + $shipping = count($this->paymill_pay_button_settings['flat_shipping']); + } }else{ - $countries = count($this->paymill_pay_button_settings['flat_shipping']); + $shipping = 5; } - for($i = 1; $i <= $countries; $i++){ - add_settings_field( 'flat_shipping_country_'.$i, __('Shipping Country', 'paymill').' #'.$i, array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_shipping',array('desc' => 'flat_shipping_country', 'option' => 'flat_shipping', 'id' => $i, 'field' => 'country')); - add_settings_field( 'flat_shipping_costs_'.$i, __('Shipping Costs', 'paymill').' '.esc_attr( $this->paymill_pay_button_settings['flat_shipping'][$i]['country'] ), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_shipping',array('desc' => 'flat_shipping_costs', 'option' => 'flat_shipping', 'id' => $i, 'field' => 'costs')); - add_settings_field( 'flat_shipping_vat_'.$i, __('Shipping VAT', 'paymill').' '.esc_attr( $this->paymill_pay_button_settings['flat_shipping'][$i]['country'] ), array( &$this, 'field_pay_button_option' ), $this->setting_keys['paymill_pay_button_settings'], 'section_pay_button_shipping',array('desc' => 'flat_shipping_vat', 'option' => 'flat_shipping', 'id' => $i, 'field' => 'vat')); - } - //} - } - - /* - * The following methods provide descriptions - * for their respective sections, used as callbacks - * with add_settings_section - */ - function section_general_desc() { echo __('Please insert your API settings here.', 'paymill'); } - function section_pay_button_desc() { echo '

'.__('Common Settings', 'paymill').'

'.'

'.__('The Paymill Pay Buton is a simple, independent payment solution.', 'paymill').'

'.__('Configure common settings', 'paymill').'
'.__('Toggle View', 'paymill').'

'.__('Products', 'paymill').'

'.__('Configure products for the Pay Button. This list has a dynamic length and extends for 5 extra slots when last slot is filled and saved.', 'paymill').'
'.__('Toggle View', 'paymill').'

'.__('Shipping', 'paymill').'

'.__('Set delivery countries and shipping costs. This list has a dynamic length and extends for 5 extra slots when last slot is filled and saved.', 'paymill').'
'.__('Toggle View', 'paymill').' - ISO 4217 e.g. "EUR" or "GBP"', 'paymill'); + $descriptions['api_key_private'] = __('Insert your Paymill PRIVATE API key.', 'paymill'); + $descriptions['api_key_public'] = __('Insert your Paymill PUBLIC API key.', 'paymill'); + + $descriptions['flat_shipping_country'] = __('Name of the available delivery country, e.g. England', 'paymill'); + $descriptions['flat_shipping_costs'] = __('Gross fee for the flat shipping costs., e.g. 7 or 4.90', 'paymill'); + $descriptions['flat_shipping_vat'] = __('Value-Added-Tax Rate in % for the flat shipping costs., e.g. 19 or 7', 'paymill'); + + $descriptions['products_title'] = __('Name of the product', 'paymill'); + $descriptions['products_desc'] = __('Detailed description of the product', 'paymill'); + $descriptions['products_price'] = __('Gross Price of the product, e.g. 40 or 6.99', 'paymill'); + $descriptions['products_offer'] = __('If you have created a subscription in your Paymill Cockpit, can select it here. If selected, it will overwrite the following settings for this product. Important: For Performance purposes, subscription plans will be cached. Open this page to recache it.', 'paymill'); + $descriptions['products_vat'] = __('Value-Added-Tax Rate in % for the product, e.g. 19 or 7', 'paymill'); + $descriptions['products_delivery'] = __('Delivery Time of the product, e.g. 2 Days or 1 Week', 'paymill'); + $descriptions['products_quantityhide'] = __('Hide quantity select field, quantity will be set to 1', 'paymill'); + $descriptions['products_freeamount'] = __('Allow free amounts (donation feature)', 'paymill'); + + if(!isset($wp_settings_fields[$page][$section])){ + return; + } + + foreach((array)$wp_settings_fields[$page][$section] as $field){ + if(isset($field['args']['group'])){ + $group_titles[$field['args']['desc']] = $field['title']; + $group_fields[$field['args']['id']][$field['args']['desc']] = $field; + }else{ + echo '
'; + echo '
'.$field['title'].'
'; + call_user_func($field['callback'], $field['args']); + if(isset($descriptions[$field['id']])){ + echo ''.$descriptions[$field['id']].''; + } + echo '
'; + } + } + if(isset($field['args']['group']) && isset($group_titles) && is_array($group_titles) && count($group_titles) > 0){ + echo ''; + echo ''; + foreach($group_titles as $name => $desc){ + echo ''; + } + echo ''; + foreach($group_fields as $id => $group){ + echo ''; + foreach($group as $group_field){ + echo ''; + } + echo ''; + } + echo '
'.$desc.'
'; + call_user_func($group_field['callback'], $group_field['args']); + echo '
'; + } + } + // Renders our tabs in the plugin options page, walks through the object's tabs array and prints them one by one. Provides the heading for the plugin_options_page method. + private function plugin_options_tabs() { + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_plugin_options_tabs'); // benchmark $current_tab = isset( $_GET['tab'] ) ? $_GET['tab'] : $this->setting_keys['paymill_general_settings']; - screen_icon(); echo ''; + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_plugin_options_tabs'); // benchmark } }; // Initialize the plugin -//add_action( 'plugins_loaded', create_function( '', '$this = new paymill_settings;' ) ); $GLOBALS['paymill_settings'] = new paymill_settings; ?> \ No newline at end of file diff --git a/lib/css/paymill.css b/lib/css/paymill.css index 0299c12..8cf1abd 100644 --- a/lib/css/paymill.css +++ b/lib/css/paymill.css @@ -1,246 +1,209 @@ /* form validation */ - #paymill_payment_form .LV_validation_message{ - font-weight:bold; - margin:0 0 0 5px; + font-weight:bold; + margin:0 0 0 5px; } - #paymill_payment_form .LV_valid { - color:#00CC00; + color:#00CC00; float:none; } - #paymill_payment_form .LV_invalid { - color:#CC0000; + color:#CC0000; float:none; } - #paymill_payment_form .LV_valid_field, #paymill_payment_form input.LV_valid_field:hover, #paymill_payment_form input.LV_valid_field:active, #paymill_payment_form textarea.LV_valid_field:hover, #paymill_payment_form textarea.LV_valid_field:active { - border: 1px solid #00CC00; -} - + border: 1px solid #00CC00; +} #paymill_payment_form .LV_invalid_field, #paymill_payment_form input.LV_invalid_field:hover, #paymill_payment_form nput.LV_invalid_field:active, #paymill_payment_form textarea.LV_invalid_field:hover, #paymill_payment_form textarea.LV_invalid_field:active { - border: 1px solid #CC0000; + border: 1px solid #CC0000; } -#paymill_payment_form input{ - -webkit-box-sizing: content-box !important; - -moz-box-sizing: content-box !important; - box-sizing: content-box !important; - margin:0px !important; +/* paymment form */ +#paymill_payment_form input, .paymill_pay_button input{ + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding:3px; + margin:0px 0px 5px 0px; + height:30px; + width:100%; + font-size:15px; } - .payment_method_paymill{ overflow:hidden; } -label[for=payment_method_paymill] *{ - overflow:hidden; - background-color:transparent; -} - -#paymill_payment_form #card-expiry-month{ - width:20px; +#paymill_payment_form #paymill_card_expiry_month{ + width:40px; text-align:center; } - -#paymill_payment_form #card-expiry-year{ - width:40px; +#paymill_payment_form #paymill_card_expiry_year{ + width:50px; text-align:center; } - -#paymill_payment_form #card-cvc{ - width:30px; +#paymill_payment_form #paymill_card_cvc{ + width:40px; text-align:center; } -#paymill_payment_form #card-number{ - background-image:url('../img/cc_logos_v.png'); +#paymill_payment_form #paymill_card_number{ + background-image:url('../img/payment_logos.png'); background-repeat:no-repeat; background-position:0px 30px; - padding-left:55px; - width:150px; + text-align:right; + font-size:90%; } -#paymill_payment_form #holdername, -#paymill_payment_form #transaction-form-account, -#paymill_payment_form #transaction-form-code{ - width:200px; -} - #paymill_framebox img{ box-shadow:none !important; } - -#form-credit, #form-elv{ +paymill_#form_credit, paymill_#form_elv{ clear:both; } - -.paymill_form-switch{ +.paymill_form_switch{ -moz-border-bottom-colors: none; - -moz-border-left-colors: none; - -moz-border-right-colors: none; - -moz-border-top-colors: none; - background-color: #F5F5F5; - background-image: linear-gradient(to bottom, #FFFFFF, #E6E6E6); - background-repeat: repeat-x; - border-color: #BBBBBB #BBBBBB #A2A2A2; - border-image: none; - border-radius: 4px 4px 4px 4px; - border-style: solid; - border-width: 1px; - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.05); - color: #333333; - cursor: pointer; - display: inline-block; - font-size: 13px; - line-height: 20px; - margin-bottom: 10px; - padding: 4px 12px; - text-align: center; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); - vertical-align: middle; + -moz-border-left-colors: none; + -moz-border-right-colors: none; + -moz-border-top-colors: none; + background-color: #F5F5F5; + background-image: linear-gradient(to bottom, #FFFFFF, #E6E6E6); + background-repeat: repeat-x; + border-color: #BBBBBB #BBBBBB #A2A2A2; + border-image: none; + border-radius: 4px; + border-style: solid; + border-width: 1px; + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.05); + color: #333333; + cursor: pointer; + display: inline-block; + font-size: 13px; + line-height: 20px; + margin-bottom: 10px; + padding: 4px 12px; + text-align: center; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: middle; +} +.paymill_form_switch:hover{ + background-color: #E6E6E6; + color: #333333; +} +.paymill_form_switch_active, .paymill_form_switch_active:hover{ + background-color: #EC4F00; + background-image: linear-gradient(to bottom, #F05000, #E64D00); + background-repeat: repeat-x; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + color: #FFFFFF; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.paymill_payment_logos{ + margin-bottom:10px; } -.paymill_form-switch:hover{ - background-color: #E6E6E6; - color: #333333; +/* error box */ +#paymill_errors{ + background-color:#FF3300; + padding:5px; + color:#FFF; + margin:10px 0px 10px 0px; + border-radius: 4px; } -.paymill_form-switch_active, .paymill_form-switch_active:hover{ - background-color: #EC4F00; - background-image: linear-gradient(to bottom, #F05000, #E64D00); - background-repeat: repeat-x; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - color: #FFFFFF; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +#paymill_errors:after{ + content:''; + display:block; + clear:both; +} +#paymill_errors .paymill_error_title{ + margin:0px 10px 0px 0px; + padding:0px; + color:#FFF; + font-weight:bold; + float:left; } -#form-credit .form-row p{ - display: inline; +#paymill_errors .paymill_error{ + color:#FFF; + float:left; } /* pay button */ .paymill_hidden{ display:none; } - -.paymill_pay_button{ - margin-bottom:50px; - border:1px solid #EC4F00; - border-radius: 15px; - padding:5px; - background-color:#E6E6E6; -} - .paymill_pay_button #place_order{ width:100%; font-weight:bold; font-size:120%; } - .paymill_products{ - margin-bottom:20px; + margin-bottom:10px; } - .paymill_pay_button .paymill_product{ - margin-bottom:5px; - padding-bottom:10px; - border-bottom:1px dashed #EC4F00; + margin-bottom:10px; +} +.paymill_pay_button .paymill_product:after{ + content:''; + display:block; + clear:both; } - .paymill_pay_button .paymill_title{ font-weight:bold; font-size:120%; margin-bottom:5px; } - .paymill_pay_button .paymill_desc{ font-size:80%; margin-bottom:5px; } - .paymill_pay_button .paymill_quantity{ - margin-bottom:5px; + margin:0px 5px 5px 0px; + float:left; + font-size:120%; } - .paymill_pay_button .paymill_price{ font-weight:bold; font-size:120%; margin-bottom:5px; + float:left; } - .paymill_pay_button .paymill_vat{ margin-bottom:5px; font-size:70%; } - .paymill_pay_button .paymill_delivery{ font-size:70%; } - .paymill_pay_button .paymill_total_price{ - margin-top:15px; font-size:120%; font-weight:bold; } - -.paymill_pay_button #paymill_payment_form *{ - font-size:100%; -} -.paymill_pay_button #paymill_payment_form label{ - display:block; -} -.paymill_pay_button #paymill_payment_form input{ +.paymill_pay_button .paymill_shipping{ margin-bottom:10px; } - .paymill_pay_button .paymill_payment_errors{ font-weight:bold; margin-bottom:5px; padding-bottom:5px; } - - -.paymill_pay_button .paymill_address .form-row { - position:relative; -} - -.paymill_pay_button .paymill_address .form-row label{ - display:block; - height:35px; - line-height:35px; -} - -.paymill_pay_button .paymill_address .form-row input{ - margin-left:auto; - margin-right:0px; - position:absolute; - right:0px; - top:0px; - width:100px; -} - .paymill_pay_button .paymill_address .paymill_address_title{ font-weight:bold; font-size:120%; border-top:1px solid #EC4F00; margin-top:5px; - padding-top:15px; + padding-top:5px; } - .paymill_pay_button .paymill_payment_title{ font-weight:bold; font-size:120%; border-top:1px solid #EC4F00; - margin-top:20px; - padding-top:15px; + margin-top:10px; + padding-top:5px; } - - -.paybutton #payment_method_paymill{ +.paymill_pay_button #payment_method_paymill{ display:none; } \ No newline at end of file diff --git a/lib/css/paymill_admin.css b/lib/css/paymill_admin.css new file mode 100644 index 0000000..dbaf15b --- /dev/null +++ b/lib/css/paymill_admin.css @@ -0,0 +1,70 @@ +.tooltip, .tooltip-2 { + width: 256px; + min-width: 256px; + position: absolute; + bottom: 0; + left: 50%; + display: none; + font-weight: normal; + font-size: 12px; + line-height: 1.5em; + color: #fff; + z-index: 50; + margin-bottom:12px; +} +.tooltip .text { + padding: 5px 15px 35px 15px; + background: url(../img/tooltip.png) no-repeat 0 100%; +} +.tooltip .top { + height: 10px; + background: url(../img/tooltip.png) no-repeat 0 0; +} + + +.paymill_settings_section_pay_button_products td > *{ + max-width:98%; +} + +/* hide quantity */ +.paymill_settings_section_pay_button_products td:nth-child(3){ + max-width:100px; +} + +/* delivery time */ +.paymill_settings_section_pay_button_products td:nth-child(4){ + max-width:100px; +} + +/* VAT */ +.paymill_settings_section_pay_button_products td:nth-child(5){ + max-width:60px; +} + +/* Price */ +.paymill_settings_section_pay_button_products td:nth-child(7){ + max-width:100px; +} + +.paymill_settings > div{ + margin-top:10px; +} +.paymill_settings div .title{ + font-weight:bold; +} + +.paymill_settings .products_name, .paymill_settings .flat_shipping_name{ + font-weight:bold; + text-align:center; +} +.paymill_settings .products_name:after, .paymill_settings .flat_shipping_name:after{ + content:'*'; +} +.payments_display{ + overflow:hidden; +} +.payments_display:after{ + content:''; + display:block; + clear:both; +} \ No newline at end of file diff --git a/lib/errors.inc.php b/lib/errors.inc.php new file mode 100644 index 0000000..f3e3bed --- /dev/null +++ b/lib/errors.inc.php @@ -0,0 +1,55 @@ +errors); + } + + public function status(){ + if($this->amount() > 0){ + return true; + } + } + + public function setFunction($function){ + if(is_string($function)){ + $this->outputFunction = $function; + return true; + }else{ + return false; + } + } + + public function getFunction(){ + return $this->outputFunction; + } + + public function setError($error){ + $this->errors[] = $error; + } + + public function getErrors($function=false,$flush=false){ + if($function === false && $this->getFunction() !== false && is_string($this->getFunction())){ + $function = $this->getFunction(); + } + + if(strlen($function) > 0){ + return $function($this->errors); + }else{ + return $this->errors; + } + if($flush === true){ + $this->errors = array(); + } + } +} + +?> \ No newline at end of file diff --git a/lib/img/cc_logos.png b/lib/img/cc_logos.png deleted file mode 100644 index 0f6ac4fa300d05fbd0b0b25d89c34666db33b9fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20955 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'; + } + + return $output; + } +} class mgm_paymill extends mgm_payment{ @@ -14,9 +26,10 @@ function __construct(){ $this->mgm_paymill(); } - // construct function mgm_paymill(){ + load_paymill(); // this function-call can and should be used whenever working with Paymill API + $GLOBALS['paymill_loader']->paymill_errors->setFunction('paymill_mgm_errorHandling'); // parent parent::__construct(); @@ -34,7 +47,7 @@ function mgm_paymill(){ $this->logo = plugins_url('',PAYMILL_DIR.'paymill.php').'/lib/img/logo.png'; // description - $this->description = __('PAYMILL - Online payments made easy', 'mgm'); + $this->description = __('PAYMILL - Online payments made easy', 'paymill'); // supported buttons types $this->supported_buttons = array('subscription', 'buypost'); @@ -52,7 +65,7 @@ function mgm_paymill(){ $this->hosted_payment = 'Y';// credit card process onsite // if supports rebill status check - $this->supports_rebill_status_check = 'Y'; + $this->supports_rebill_status_check = 'N'; // default settings $this->_default_setting(); @@ -65,7 +78,6 @@ function mgm_paymill(){ } // MODULE API COMMON HOOKABLE CALLBACKS ////////////////////////////////////////////////////////////////// - // settings function settings(){ @@ -81,7 +93,6 @@ function settings(){ $this->load->template('settings', array('data'=>$data)); } - // settings_box function settings_box(){ @@ -96,7 +107,6 @@ function settings_box(){ // load template view return $this->load->template('settings_box', array('data'=>$data), true); } - // update function settings_update(){ @@ -231,7 +241,6 @@ function settings_update(){ } } - // return process api hook, link back to site after payment is made function process_return(){ if(!isset($this->response)) $this->response = array(); @@ -265,17 +274,128 @@ function process_return(){ mgm_redirect(add_query_arg(array('status'=>'error','errors'=>urlencode($this->process_payment())), $this->_get_thankyou_url())); } } + private function getCurrentClient(){ + require_once(PAYMILL_DIR.'lib/integration/client.inc.php'); + // create or get client + $this->clientClass = new paymill_client($this->user->data->user_email,$this->user->data->user_login); + return $this->clientClass->getCurrentClient(); + } + private function processSubscriptions(){ + global $wpdb; + + // product is a subscription? + if(isset($this->order['payment_type']) && $this->order['payment_type'] == 'subscription_purchase' && + isset($this->durations[$this->order['duration_type']]) && strlen($this->durations[$this->order['duration_type']]) > 0){ + // required vars + $trial_time = false; // MGM does not support trials?! + + // set interval + $interval = $this->order['duration'].' '.$this->durations[$this->order['duration_type']]; + + // md5 name + $mgm_sub_md5 = md5($this->total.$this->order['currency'].$interval.$trial_time); + + // get offer + $name = 'mgm_'.$this->order['pack_id'].'_'.$mgm_sub_md5; + $offer = $this->subscriptions->offerGetDetailByName($name); + + // check wether offer exists in paymill + if($offer === false){ + // offer does not exist in paymill yet, create it + $params = array( + 'amount' => $this->total, + 'currency' => $this->order['currency'], + 'interval' => $interval, + 'name' => $name, + 'trial_period_days' => intval($trial_time) + ); + $offer = $this->subscriptions->offerCreate($params); + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + return false; + } + } + + // create user subscription + $user_sub = $this->subscriptions->create($this->client->getId(), $offer['id'], $this->paymentClass->getPaymentID()); + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + return false; + }else{ + $wpdb->query($wpdb->prepare('INSERT INTO '.$wpdb->prefix.'paymill_subscriptions (paymill_sub_id, mgm_user_id, mgm_offer_id) VALUES (%s, %s, %s)', + array( + $user_sub, + get_current_user_id(), + $this->order['pack_id'] + ))); + + // subscription successful + do_action('paymill_mgm_subscription_created', array( + 'product_id' => $this->order['pack_id'], + 'offer_id' => $offer['id'], + 'offer_data' => $offer + )); + + return true; + } + }else{ + return true; + } + } + private function processProducts(){ + global $wpdb; + if($this->total > 0 && isset($this->order['payment_type']) && $this->order['payment_type'] == 'post_purchase' || (isset($this->order['num_cycles']) && $this->order['num_cycles'] == 1)){ + // make transaction + $GLOBALS['paymill_loader']->request_transaction->setAmount(round($this->total)); // e.g. "4200" for 42.00 EUR + $GLOBALS['paymill_loader']->request_transaction->setCurrency($this->order['currency']); + if($this->paymentClass->getPreauthID() != false){ + $GLOBALS['paymill_loader']->request_transaction->setPreauthorization($this->paymentClass->getPreauthID()); + }else{ + $GLOBALS['paymill_loader']->request_transaction->setPayment($this->paymentClass->getPaymentID()); + } + $GLOBALS['paymill_loader']->request_transaction->setClient($this->client->getId()); + $GLOBALS['paymill_loader']->request_transaction->setDescription($this->order_desc); + $GLOBALS['paymill_loader']->request->setSource(serialize($GLOBALS['paymill_source'])); + + $GLOBALS['paymill_loader']->request->create($GLOBALS['paymill_loader']->request_transaction); + $response = $GLOBALS['paymill_loader']->request->getLastResponse(); + + if(isset($response['body']['data']['response_code']) && $response['body']['data']['response_code'] != '20000'){ + $GLOBALS['paymill_loader']->paymill_errors->setError(__($response['body']['data']['response_code'], 'paymill')); + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + $GLOBALS['paymill_loader']->paymill_errors->getErrors(); + } + return false; + } + + // save data to transaction table + $wpdb->query($wpdb->prepare(' + INSERT INTO '.$wpdb->prefix.'paymill_transactions (paymill_transaction_id, paymill_payment_id, paymill_client_id, mgm_order_id, paymill_transaction_time, paymill_transaction_data) + VALUES (%s,%s,%s,%d,%d,%s)', + array( + $response['body']['data']['id'], + $response['body']['data']['payment']['id'], + $response['body']['data']['client']['id'], + $this->order_id, + time(), + serialize($_POST) + ))); + + do_action('paymill_mgm_products_paid', array( + 'total' => $this->total, + 'currency' => $this->order['currency'], + 'client' => $response['body']['data']['client']['id'] + )); + + return true; + }else{ // total is zero, so just return true + return true; + } + } // make payment + // @ PAYMILL function process_payment(){ global $wpdb; - //ini_set('display_errors',1); - //error_reporting(E_ALL); - - //var_dump($_POST); - // $_POST[''] array(3) { ["tran_id"]=> string(2) "29" ["submit_from"]=> string(21) "process_html_redirect" ["paymillToken"]=> string(24) "tok_8ae4ec7be24098a4ed77" } - //record POST/GET data do_action('mgm_print_module_data', $this->module, __FUNCTION__ ); @@ -342,7 +462,6 @@ function process_payment(){ */ $transaction = $this->_get_transaction_passthrough($_POST['tran_id']); - /* var_dump(get_userdata($transaction['user_id'])); @@ -752,171 +871,84 @@ function process_payment(){ } */ - $user = get_userdata($transaction['user_id']); - - // first retrieve client data, either from cache or from API - require_once(PAYMILL_DIR.'lib/integration/client.inc.php'); - $clientClass = new paymill_client( - $user->data->user_email, - $user->data->user_login - ); - - $client = $clientClass->getCurrentClient(); + $this->user = get_userdata($transaction['user_id']); + $this->client = $this->getCurrentClient(); // client retrieved, now we are ready to process the payment - if($client['id'] !== false && strlen($client['id']) > 0){ - require_once(PAYMILL_DIR.'lib/integration/payment.inc.php'); - //die('payment start'); - if(!isset($this->paymentClass)){ - $this->paymentClass = new paymill_payment($client['id']); - } - - // mapping - $durations = array( - 'd' => 'DAY', - 'w' => 'WEEK', - 'm' => 'MONTH', - 'y' => 'YEAR' - ); + if($this->client->getId() !== false && strlen($this->client->getId()) > 0){ + $this->order_id = $_POST['tran_id']; + $this->order_desc = __('Order #','paymill').$this->order_id; + $this->order = $transaction; + $this->total_complete = + $this->total = round((floatval($transaction['cost'])*100)); + // mapping + $this->durations = array( + 'd' => 'DAY', + 'w' => 'WEEK', + 'm' => 'MONTH', + 'y' => 'YEAR' + ); // l = lifetime // one time purchase // dr = daterange // one time purchase + + // load subscription class + $this->subscriptions = new paymill_subscriptions('mgm'); + $this->offers = $this->subscriptions->offerGetList(); + + // get the totals for pre authorization + // $this->getTotals(); + + // create payment object and preauthorization + require_once(PAYMILL_DIR.'lib/integration/payment.inc.php'); + $this->paymentClass = new paymill_payment($this->client->getId(),$this->total_complete,$this->order['currency']); // create payment object, as it should be used for next processing instead of the token. + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + $GLOBALS['paymill_loader']->paymill_errors->getErrors(); + return false; + } - // test vars - /*var_dump($client['id']); - var_dump($this->paymentClass->getPaymentID()); - var_dump(is_array($transaction)); - var_dump($transaction['payment_type']); - var_dump(isset($durations[$transaction['duration_type']])); - die();*/ - - $paymentID = $this->paymentClass->getPaymentID(); - - // make subscription - if( - isset($client['id']) && strlen($client['id']) > 0 && - isset($paymentID) && strlen($paymentID) > 0 && - is_array($transaction) && count($transaction) > 0 && - isset($transaction['payment_type']) && $transaction['payment_type'] == 'subscription_purchase' && - isset($durations[$transaction['duration_type']]) && strlen($durations[$transaction['duration_type']]) > 0 - ){ - $subscriptions = new paymill_subscriptions('magicmembers'); - - // required vars - $amount = (floatval($transaction['cost'])*100); - $currency = $transaction['currency']; - - // set interval - $interval = $transaction['duration'].' '.$durations[$transaction['duration_type']]; - - // get trial time - // $now = time(); - // $trial_end = strtotime(WC_Subscriptions_Product::get_trial_expiration_date($product['product_id'], get_gmt_from_date($order->order_date))); - // $datediff = $trial_end - $now; - // $trial_time = ceil($datediff/(60*60*24)); - - $trial_time = false; // MGM does not support trials?! - - // md5 name - $mgm_sub_md5 = md5($amount.$currency.$interval.$trial_time); - - // get offer - $name = 'mgm_'.$transaction['pack_id'].'_'.$mgm_sub_md5; - $offer = $subscriptions->offerGetDetailByName($name); - $offer = $offer[0]; - - // check wether offer exists in paymill - if(count($offer) == 0){ - // offer does not exist in paymill yet, create it - $params = array( - 'amount' => $amount, - 'currency' => $currency, - 'interval' => $interval, - 'name' => $name, - 'trial_period_days' => $trial_time - ); - $offer = $subscriptions->offerCreate($params); - - if(isset($offer['error']['messages'])){ - foreach($offer['error']['messages'] as $field => $msg){ - $woocommerce->add_error($field.': '.$msg); - } - return; - } - } - - // create user subscription - $user_sub = $subscriptions->create($client['id'], $offer['id'], $paymentID); + // process subscriptions & products + if($this->processSubscriptions() && $this->processProducts()){ + // success + // hook for pre process + $this->response['response_status'] = 'Approved'; - if(isset($user_sub['error']) && strlen($user_sub['error']) > 0){ - return __($user_sub['error'], 'paymill'); - }else{ - $query = 'INSERT INTO '.$wpdb->prefix.'paymill_subscriptions (paymill_sub_id, mgm_user_id, mgm_offer_id) VALUES ("'.$user_sub['id'].'", "'.$transaction['user_id'].'", "'.$transaction['pack_id'].'")'; - $wpdb->query($query); - - // subscription successful - do_action('paymill_mgm_subscription_created', array( - 'product_id' => $id, - 'offer_id' => $offer['id'], - 'offer_data' => $offer - )); - } - }else{ - die('ende'); - if(!isset($client['id']) || strlen($client['id']) <= 0){ - $error .= '

no client ID: '.$client['id'].'

'; - } - if(!$paymentID){ - $error .= '

no payment ID: '.$paymentID.'

'; - } - if(!is_array($transaction) || count($transaction) <= 0){ - $error .= '

no transaction: '.count($transaction).'

'; - } - if(!isset($transaction['payment_type']) || $transaction['payment_type'] != 'subscription_purchase'){ - $error .= '

no supported payment type: '.$transaction['payment_type'].'

'; - } - if(!isset($durations[$transaction['duration_type']]) || strlen($durations[$transaction['duration_type']]) <= 0){ - $error .= '

no duration type: '.$durations[$transaction['duration_type']].'

'; - } - - - return '

payment initialization failed

'.$error; - } - }else{ - return 'client creation failed'; - } - - - // hook for pre process - do_action('mgm_notify_pre_process_'.$this->module, array('tran_id'=>$_POST['tran_id'],'custom'=>$transaction)); + do_action('mgm_notify_pre_process_'.$this->module, array('tran_id'=>$_POST['tran_id'],'custom'=>$transaction)); - // check - switch($transaction['payment_type']){ + // check + switch($transaction['payment_type']){ + // buypost + case 'post_purchase': + case 'buypost': + $this->_buy_post(); //run the code to process a purchased post/page - // buypost - case 'post_purchase': - case 'buypost': - $this->_buy_post(); //run the code to process a purchased post/page + break; - break; + // subscription + case 'subscription_purchase': + $this->_buy_membership(); //run the code to process a new/extended membership - // subscription - case 'subscription_purchase': - $this->_buy_membership(); //run the code to process a new/extended membership + break; + } - break; + // after process + do_action('mgm_notify_post_process_'.$this->module, array('tran_id'=>$_POST['tran_id'],'custom'=>$transaction)); + // after process unverified + do_action('mgm_notify_post_process_unverified_'.$this->module); + + return true; + }else{ + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + $GLOBALS['paymill_loader']->paymill_errors->getErrors(); + } + return false; + } + }else{ + $GLOBALS['paymill_loader']->paymill_errors->setError(__('There was an issue with adding you as client for the payment process.', 'paymill')); + return false; } - - // after process - do_action('mgm_notify_post_process_'.$this->module, array('tran_id'=>$_POST['tran_id'],'custom'=>$transaction)); - - // after process unverified - do_action('mgm_notify_post_process_unverified_'.$this->module); - - return true; } - // process cancel api hook function process_cancel(){ @@ -925,7 +957,6 @@ function process_cancel(){ mgm_redirect(add_query_arg(array('status'=>'cancel'), $this->_get_thankyou_url())); } - // unsubscribe process, proxy for unsubscribe function process_unsubscribe() { @@ -1015,7 +1046,6 @@ function process_unsubscribe() { mgm_redirect(mgm_get_custom_url('membership_details', false,array('unsubscribe_errors'=>urlencode($message)))); } - // PAYMENT CALL - payment starts here function process_credit_card(){ @@ -1294,10 +1324,10 @@ function process_credit_card(){ // $this->process_return(); } - // process html_redirect, proxy for form submit //The credit card form will get submitted to the same function, then validate the card and if everything is clear //() will be called internally + // @ PAYMILL function process_html_redirect(){ // read tran id @@ -1378,6 +1408,8 @@ function process_html_redirect(){ ob_start(); if(!$GLOBALS['paymill_active']){ + paymill_load_frontend_scripts(); // load frontend scripts + // settings $GLOBALS['paymill_active'] = true; $country = 'DE'; @@ -1410,9 +1442,9 @@ function process_html_redirect(){ return $html; - } - + } // subscribe button api hook + // @ PAYMILL function get_button_subscribe($options=array()){ $include_permalink = (isset($options['widget'])) ? false : true; @@ -1441,8 +1473,8 @@ function get_button_subscribe($options=array()){ return $html; } - // buypost button api hook + // @ PAYMILL function get_button_buypost($options=array(), $return = false) { // get html @@ -1477,7 +1509,6 @@ function get_button_buypost($options=array(), $return = false) { } } - // unsubscribe button api hook function get_button_unsubscribe($options=array()){ @@ -1514,7 +1545,6 @@ function get_button_unsubscribe($options=array()){ return $html; } - // get module transaction info function get_transaction_info($member, $date_format){ @@ -1542,7 +1572,7 @@ function get_transaction_info($member, $date_format){ // info - $info = sprintf('%s:
%s: %s
%s: %s', __('AUTHORIZE.NET INFO','mgm'), __('SUBSCRIPTION ID','mgm'), $subscription_id, + $info = sprintf('%s:
%s: %s
%s: %s', __('Paymill INFO','paymill'), __('SUBSCRIPTION ID','mgm'), $subscription_id, __('TRANSACTION ID','mgm'), $transaction_id); @@ -1557,7 +1587,6 @@ function get_transaction_info($member, $date_format){ return $transaction_info; } - /** * get gateway tracking fields for sync @@ -1567,7 +1596,6 @@ function get_transaction_info($member, $date_format){ * @todo process another subscription */ - function get_tracking_fields_html(){ // html @@ -1585,7 +1613,6 @@ function get_tracking_fields_html(){ return $html; } - /** * update and sync gateway tracking fields @@ -1601,7 +1628,6 @@ function get_tracking_fields_html(){ * @uses _save_tracking_fields() */ - function update_tracking_fields($post_data, &$member){ // validate @@ -1623,11 +1649,7 @@ function update_tracking_fields($post_data, &$member){ return $this->_save_tracking_fields($fields, $member, $data); } - - - // MODULE API COMMON PRIVATE HELPERS ///////////////////////////////////////////////////////////////// - // get button data function _get_button_data($pack, $tran_id=NULL) { @@ -1786,12 +1808,8 @@ function _get_button_data($pack, $tran_id=NULL) { return $data; } - // buy post @todo !!! function _buy_post() { - - - global $wpdb; // system @@ -1842,7 +1860,7 @@ function _buy_post() { // response code - $response_code = $this->_get_response_code($this->response['response_status'], 'status'); + $response_code = $this->response['response_status']; // process on response code @@ -2097,7 +2115,6 @@ function _buy_post() { } } - function _get_alternate_transaction_id(){ // var @@ -2123,7 +2140,6 @@ function _get_alternate_transaction_id(){ return $alt_tran_id; } - // buy membership @todo !!! function _buy_membership() { @@ -2833,7 +2849,6 @@ function _buy_membership() { } } - // cancel membership function _cancel_membership($user_id, $redirect = false){ @@ -3068,9 +3083,6 @@ function _cancel_membership($user_id, $redirect = false){ } } - - - /** * Cancel Recurring Subscription @@ -3179,15 +3191,13 @@ function cancel_recurring_subscription($trans_ref = null, $user_id = null, $subs //only for subscription_purchase if($pack_id && $user_id){ - $userInfo = get_userdata($user_id); - $subscriptions = new paymill_subscriptions('magicmembers'); - + $query = 'SELECT paymill_sub_id FROM '.$wpdb->prefix.'paymill_subscriptions WHERE mgm_user_id="'.$user_id.'" AND mgm_offer_id="'.$pack_id.'"'; $client_cache = $wpdb->get_results($query,ARRAY_A); if(isset($client_cache[0]['paymill_sub_id']) && strlen($client_cache[0]['paymill_sub_id']) > 0){ - $subscriptions->remove($client_cache[0]['paymill_sub_id']); + $this->subscriptions->remove($client_cache[0]['paymill_sub_id']); $query = 'DELETE FROM '.$wpdb->prefix.'paymill_subscriptions WHERE mgm_user_id="'.$user_id.'" AND mgm_offer_id="'.$pack_id.'"'; $wpdb->query($query); @@ -3201,9 +3211,6 @@ function cancel_recurring_subscription($trans_ref = null, $user_id = null, $subs return false; } - - - /** * Specifically check recurring status of each rebill for an expiry date @@ -3217,7 +3224,6 @@ function cancel_recurring_subscription($trans_ref = null, $user_id = null, $subs * @return boolean */ - function query_rebill_status($user_id, $member=NULL) { // check @@ -3483,7 +3489,6 @@ function query_rebill_status($user_id, $member=NULL) { return false;//default to false to skip normal modules } - // get transaction function get_transaction_details($member){ @@ -3548,7 +3553,6 @@ function get_transaction_details($member){ return $response; } - // default setting function _default_setting(){ @@ -3575,7 +3579,6 @@ function _default_setting(){ $this->_setup_callback_urls(); } - // log transaction function _log_transaction(){ @@ -3632,7 +3635,6 @@ function _log_transaction(){ return false; } - // get tran id function _get_transaction_id(){ diff --git a/lib/integration/pay_button.inc.php b/lib/integration/pay_button.inc.php index d8311ab..6c68b91 100644 --- a/lib/integration/pay_button.inc.php +++ b/lib/integration/pay_button.inc.php @@ -1,132 +1,188 @@
'.__('Error:', 'paymill').'
'; + + foreach($errors as $error){ + $output .= '
'.$error.'
'; + } + + $output .= ''; + + return $output; + } + } + + class paymill_pay_button_processPayment{ + private $order_id = false; + private $order_desc = ''; + private $total = 0; + private $total_complete = 0; + private $client = false; + private $paymentClass = false; - // first retrieve client data, either from cache or from API + public function __construct(){ + load_paymill(); // this function-call can and should be used whenever working with Paymill API + $GLOBALS['paymill_loader']->paymill_errors->setFunction('paymill_pay_button_errorHandling'); + $GLOBALS['paymill_source']['pay_button_version'] = PAYMILL_VERSION; + $this->order_id = time(); + $this->order_desc = apply_filters('paymill_paybutton_order_desc', __('Order', 'paymill').' #'.$this->order_id, array($this->order_id, $_POST)); + } + private function getCurrentClient(){ require_once(PAYMILL_DIR.'lib/integration/client.inc.php'); - $clientClass = new paymill_client( - $_POST['email'], - $_POST['forename'].' '.$_POST['surname'] - ); + if(isset($_POST['forename']) && isset($_POST['surname'])){ + $desc = $_POST['forename'].' '.$_POST['surname']; + }elseif(isset($_POST['forename'])){ + $desc = $_POST['forename']; + }elseif(isset($_POST['surname'])){ + $desc = $_POST['surname']; + }else{ + $desc = ''; + } - $client = $clientClass->getCurrentClient(); + $desc = apply_filters('paymill_paybutton_client_desc', $desc, array($this->order_id, $_POST)); - // client retrieved, now we are ready to process the payment - if($client['id'] !== false && strlen($client['id']) > 0){ - require_once(PAYMILL_DIR.'lib/integration/payment.inc.php'); + // create or get client + $this->clientClass = new paymill_client($_POST['email'],$desc); + return $this->clientClass->getCurrentClient(); + } + private function getTotals(){ + // load subscription class + $this->subscriptions = new paymill_subscriptions('pay_button'); + $offers = $this->subscriptions->offerGetList(); + + foreach($_POST['paymill_quantity'] as $id => $quantity){ + if( + isset($GLOBALS['paymill_settings']->paymill_pay_button_settings['products'][$id]['products_offer']) && + isset($offers[$GLOBALS['paymill_settings']->paymill_pay_button_settings['products'][$id]['products_offer']]['amount']) && + floatval($offers[$GLOBALS['paymill_settings']->paymill_pay_button_settings['products'][$id]['products_offer']]['amount']) > 0 + ){ + // retrieve subscription amount + $amount = floatval( + floatval($offers[$GLOBALS['paymill_settings']->paymill_pay_button_settings['products'][$id]['products_offer']]['amount']) + *intval($quantity) + ); + }else{ + // retrieve product amount + $amount = floatval( + floatval($GLOBALS['paymill_settings']->paymill_pay_button_settings['products'][$id]['products_price']) + *intval($quantity) + *100 + ); + $this->total = $this->total+$amount; + } - $paymentClass = new paymill_payment($client['id']); - // calculate total based on product settings - if(isset($_POST['paymill_quantity']) && count($_POST['paymill_quantity']) > 0){ - $total = 0; - $subscriptions = false; - foreach($_POST['paymill_quantity'] as $id => $quantity){ - // item is subscription, so don't add amount to total calculation - if(isset($_POST['paymill_offer'][$id])){ - // create subscription - if(isset($_POST['paymill_quantity'][$id]) && $_POST['paymill_quantity'][$id] == 1){ - if($subscriptions === false){ - $subscriptions = new paymill_subscriptions('pay_button'); - } - - $offer = $subscriptions->create($client['id'], $_POST['paymill_offer'][$id], $paymentClass->getPaymentID()); - - //var_dump($offer); - // offer cannot be subscribed. - if(isset($offer['error']) && strlen($offer['error']) > 0){ - echo __($offer['error'], 'paymill'); - die(); - }else{ // subscription successful - do_action('paymill_paybutton_subscription_created', array( - 'product_id' => $id, - 'offer_id' => $_POST['paymill_offer'][$id], - 'offer_data' => $offer - )); - } - } - }else{ - // retrieve product price and add to total - $total = ($total+floatval($GLOBALS['paymill_settings']->paymill_pay_button_settings['products'][$id]['price'])*intval($quantity)); - } - } - // now we have total amount of all non-subscription products. Time to make transaction. - if($total > 0){ - $transactionsObject = new Services_Paymill_Transactions($GLOBALS['paymill_settings']->paymill_general_settings['api_key_private'], $GLOBALS['paymill_settings']->paymill_general_settings['api_endpoint']); - - $order_id = time(); + $this->total_complete = $this->total_complete+$amount; + } - // make transaction - $order_desc = apply_filters( 'paymill_paybutton_order_desc', __('Order', 'paymill').' #'.$order_id."\n\n", array($order_id, $transaction, $_POST, $order_mail)); - - $params = array( - 'amount' => $total*100, // e.g. "4200" for 42.00 EUR - 'currency' => $GLOBALS['paymill_settings']->paymill_general_settings['currency'], // ISO 4217 - 'payment' => $paymentClass->getPaymentID(), - 'client' => $client['id'], - 'description' => $order_desc, - 'source' => serialize($GLOBALS['paymill_source']) - ); - $transaction = $transactionsObject->create($params); + // add shipping rate + if(isset($_POST['paymill_shipping']) && strlen($_POST['paymill_shipping']) > 0){ + $shipping_costs = (floatval($GLOBALS['paymill_settings']->paymill_pay_button_settings['flat_shipping'][intval($_POST['paymill_shipping'])]['flat_shipping_costs'])*100); + $this->total = $this->total+$shipping_costs; + $this->total_complete = $this->total_complete+$shipping_costs; + } + } + private function processSubscriptions(){ + foreach($_POST['paymill_quantity'] as $id => $quantity){ + if(isset($_POST['paymill_offer'][$id])){ + // create subscription + if(isset($_POST['paymill_quantity'][$id]) && $_POST['paymill_quantity'][$id] == 1){ + $offer = $this->subscriptions->create($this->client->getId(), $_POST['paymill_offer'][$id], $this->paymentClass->getPaymentID()); - $response = $transactionsObject->getResponse(); - if(isset($response['body']['data']['response_code']) && $response['body']['data']['response_code'] != '20000'){ - echo __($response['body']['data']['response_code'], 'paymill'); - die(); + // offer cannot be subscribed. + if($offer === false){ + return false; + }else{ // subscription successful + do_action('paymill_paybutton_subscription_created', array( + 'product_id' => $id, + 'offer_id' => $_POST['paymill_offer'][$id], + 'offer_data' => $offer + )); } - - // save data to transaction table - $query = 'INSERT INTO '.$wpdb->prefix.'paymill_transactions (paymill_transaction_id, paymill_payment_id, paymill_client_id, pay_button_order_id, paymill_transaction_time, paymill_transaction_data) VALUES ("'.$transaction['id'].'", "'.$transaction['payment']['id'].'", "'.$transaction['client']['id'].'", "'.$order_id.'", "'.$order_id.'", "'.$wpdb->escape(serialize($_POST)).'")'; - $wpdb->query($query); - - do_action('paymill_paybutton_products_paid', array( - 'total' => $total, - 'currency' => $GLOBALS['paymill_settings']->paymill_general_settings['currency'], - 'client' => $client['id'] - )); } - + }else{ // no subscriptions in cart, so just return true + return true; } - }else{ - echo __('There was an issue with adding you as client for the payment process.', 'paymill'); - die(); } - - // order complete - do_action( 'paymill_paybutton_order_complete', array($order_id, $transaction, $_POST) ); - - // prepare order confirmation mail - if(!isset($order_desc)){ - $order_desc = ''; + return true; + } + private function processProducts(){ + global $wpdb; + if($this->total > 0){ + // make transaction + $GLOBALS['paymill_loader']->request_transaction->setAmount(round($this->total)); // e.g. "4200" for 42.00 EUR + $GLOBALS['paymill_loader']->request_transaction->setCurrency($GLOBALS['paymill_settings']->paymill_pay_button_settings['currency']); + if($this->paymentClass->getPreauthID() != false){ + $GLOBALS['paymill_loader']->request_transaction->setPreauthorization($this->paymentClass->getPreauthID()); + }else{ + $GLOBALS['paymill_loader']->request_transaction->setPayment($this->paymentClass->getPaymentID()); + } + $GLOBALS['paymill_loader']->request_transaction->setClient($this->client->getId()); + $GLOBALS['paymill_loader']->request_transaction->setDescription($this->order_desc); + $GLOBALS['paymill_loader']->request->setSource(serialize($GLOBALS['paymill_source'])); + + $GLOBALS['paymill_loader']->request->create($GLOBALS['paymill_loader']->request_transaction); + + $response = $GLOBALS['paymill_loader']->request->getLastResponse(); + + if(isset($response['body']['data']['response_code']) && $response['body']['data']['response_code'] != '20000'){ + $GLOBALS['paymill_loader']->paymill_errors->setError(__($response['body']['data']['response_code'], 'paymill')); + return false; + } + + // save data to transaction table + $wpdb->query($wpdb->prepare(' + INSERT INTO '.$wpdb->prefix.'paymill_transactions (paymill_transaction_id, paymill_payment_id, paymill_client_id, pay_button_order_id, paymill_transaction_time, paymill_transaction_data) + VALUES (%s,%s,%s,%d,%d,%s)', + array( + $response['body']['data']['id'], + $response['body']['data']['payment']['id'], + $response['body']['data']['client']['id'], + $this->order_id, + $this->order_id, + serialize($_POST) + ))); + + do_action('paymill_paybutton_products_paid', array( + 'total' => $this->total, + 'currency' => $GLOBALS['paymill_settings']->paymill_pay_button_settings['currency'], + 'client' => $response['body']['data']['client']['id'] + )); + + return true; + }else{ // total is zero, so just return true + return true; } + } + private function sendMail(){ + $this->order_desc .= "\n\n"; // customer details - $email_customer_desc = __('Company Name', 'paymill').': '.strip_tags($_POST['company_name'])."\n". - __('Forename', 'paymill').': '.strip_tags($_POST['forename'])."\n". - __('Surname', 'paymill').': '.strip_tags($_POST['surname'])."\n". - __('Street', 'paymill').': '.strip_tags($_POST['street'])."\n". - __('Number', 'paymill').': '.strip_tags($_POST['number'])."\n". - __('ZIP', 'paymill').': '.strip_tags($_POST['zip'])."\n". - __('City', 'paymill').': '.strip_tags($_POST['city'])."\n". - __('Country', 'paymill').': '.strip_tags($_POST['paymill_shipping'])."\n". - __('Email', 'paymill').': '.strip_tags($_POST['email'])."\n". - __('Phone', 'paymill').': '.strip_tags($_POST['phone'])."\n\n"; + $email_customer_desc = + (isset($_POST['company_name']) ? (__('Company Name', 'paymill').': '.strip_tags($_POST['company_name'])."\n") : ''). + (isset($_POST['forename']) ? (__('Forename', 'paymill').': '.strip_tags($_POST['forename'])."\n") : ''). + (isset($_POST['surname']) ? (__('Surname', 'paymill').': '.strip_tags($_POST['surname'])."\n") : ''). + (isset($_POST['street']) ? (__('Street', 'paymill').': '.strip_tags($_POST['street'])."\n") : ''). + (isset($_POST['number']) ? (__('Number', 'paymill').': '.strip_tags($_POST['number'])."\n") : ''). + (isset($_POST['zip']) ? (__('ZIP', 'paymill').': '.strip_tags($_POST['zip'])."\n") : ''). + (isset($_POST['city']) ? (__('City', 'paymill').': '.strip_tags($_POST['city'])."\n") : ''). + (isset($_POST['paymill_shipping']) && isset($GLOBALS['paymill_settings']->paymill_pay_button_settings['flat_shipping'][$_POST['paymill_shipping']]) ? (__('Country', 'paymill').': '.strip_tags($GLOBALS['paymill_settings']->paymill_pay_button_settings['flat_shipping'][$_POST['paymill_shipping']]['flat_shipping_country'])."\n") : ''). + (isset($_POST['email']) ? (__('Email', 'paymill').': '.strip_tags($_POST['email'])."\n") : ''). + (isset($_POST['phone']) ? (__('Phone', 'paymill').': '.strip_tags($_POST['phone'])."\n") : ''); // products details + $order_products = ''; foreach($_POST['paymill_quantity'] as $product => $quantity){ if(intval($quantity) > 0){ - $order_products .= $quantity.'x '.$GLOBALS['paymill_settings']->paymill_pay_button_settings['products'][$product]['title']."\n"; + $order_products .= $quantity.'x '.$GLOBALS['paymill_settings']->paymill_pay_button_settings['products'][$product]['products_title']."\n"; } } - $order_mail = $order_desc.$email_customer_desc.$order_products; + $order_mail = $this->order_desc.$email_customer_desc.$order_products; // allow filtering the order email - $order_mail = apply_filters( 'paymill_paybutton_email_text', $order_mail, array($order_id, $transaction, $_POST, $order_mail)); + $order_mail = apply_filters( 'paymill_paybutton_email_text', $order_mail, array($this->order_id, $_POST, $order_mail)); // send confirmation mail wp_mail( @@ -143,17 +199,56 @@ function paymill_pay_button_process_payment(){ ); // order complete - do_action( 'paymill_paybutton_email_sent', array($order_id, $transaction, $_POST, $order_mail) ); - - // success, redirect if thankyou url is set - if(isset($GLOBALS['paymill_settings']->paymill_pay_button_settings['thankyou_url']) && strlen($GLOBALS['paymill_settings']->paymill_pay_button_settings['thankyou_url']) > 0){ - header('Location: '.$GLOBALS['paymill_settings']->paymill_pay_button_settings['thankyou_url']); - die(); + do_action('paymill_paybutton_email_sent', array($this->order_id, $_POST, $order_mail)); + } + public function process_payment(){ + global $wpdb; + if(isset($_POST['paymill_quantity']) && count($_POST['paymill_quantity']) > 0){ + $this->client = $this->getCurrentClient(); + + // client retrieved, now we are ready to process the payment + if($this->client->getId() !== false && strlen($this->client->getId()) > 0){ + // get the totals for pre authorization + $this->getTotals(); + + // create payment object and preauthorization + require_once(PAYMILL_DIR.'lib/integration/payment.inc.php'); + $this->paymentClass = new paymill_payment($this->client->getId(),$this->total_complete,$GLOBALS['paymill_settings']->paymill_pay_button_settings['currency']); // create payment object, as it should be used for next processing instead of the token. + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + return false; + } + + // process subscriptions & products + if($this->processSubscriptions() && $this->processProducts()){ + // order complete + do_action('paymill_paybutton_order_complete', array($this->order_id, $_POST)); + + // prepare order confirmation mail + $this->sendMail(); + + // success, redirect if thankyou url is set + if(isset($GLOBALS['paymill_settings']->paymill_pay_button_settings['thankyou_url']) && strlen($GLOBALS['paymill_settings']->paymill_pay_button_settings['thankyou_url']) > 0){ + header('Location: '.$GLOBALS['paymill_settings']->paymill_pay_button_settings['thankyou_url']); + die(); + }else{ // shpw thank you message and hide form + define('PAYMILL_PAYBUTTON_ORDER_SUCCESS', true); + } + }else{ + return false; + } + }else{ + $GLOBALS['paymill_loader']->paymill_errors->setError(__('There was an issue with adding you as client for the payment process.', 'paymill')); + return false; + } } } } - - add_action('plugins_loaded', 'paymill_pay_button_process_payment'); + if(isset($_REQUEST['paymill_pay_button_order']) && $_REQUEST['paymill_pay_button_order'] == 1){ + add_action('plugins_loaded', function(){ + $class = new paymill_pay_button_processPayment(); + $class->process_payment(); + }); + } class paymill_pay_button_widget extends WP_Widget{ var $subscriptions = false; @@ -161,6 +256,9 @@ class paymill_pay_button_widget extends WP_Widget{ /** constructor */ function __construct() { parent::WP_Widget('paymill_pay_button_widget', 'Paymill Pay Button', array( 'description' => __('Shows a Paymill Payment Button.', 'paymill'))); + + load_paymill(); // this function-call can and should be used whenever working with Paymill API + $GLOBALS['paymill_loader']->paymill_errors->setFunction('paymill_pay_button_errorHandling'); } function widget($args, $instance){ global $wpdb; @@ -169,6 +267,8 @@ function widget($args, $instance){ isset($GLOBALS['paymill_settings']->paymill_pay_button_settings['products']) && count($GLOBALS['paymill_settings']->paymill_pay_button_settings['products']) > 0 ){ + paymill_load_frontend_scripts(); // load frontend scripts + $GLOBALS['paymill_active'] = true; echo $args['before_widget']; @@ -176,20 +276,20 @@ function widget($args, $instance){ if(strlen($instance['title']) > 0){ echo $args['before_title']; ?>paymill_general_settings['currency']; - $cc_logo = plugins_url('',__FILE__ ).'/../img/cc_logos_v.png'; - $title = apply_filters( 'widget_title', $instance['title'] ); - + $currency = $GLOBALS['paymill_settings']->paymill_pay_button_settings['currency']; + $cc_logo = plugins_url('',__FILE__ ).'/../img/cc_logos_v.png'; + $title = apply_filters( 'widget_title', $instance['title'] ); + $show_fields = isset($GLOBALS['paymill_settings']->paymill_pay_button_settings['fields_show']) ? $GLOBALS['paymill_settings']->paymill_pay_button_settings['fields_show'] : false; + if(isset($instance['products_list']) && strlen($instance['products_list']) > 0){ - - $products_whitelist = explode(',',$instance['products_list']); + $products_whitelist = explode(',',$instance['products_list']); }else{ - $products_whitelist = unserialize($instance['products']); + $products_whitelist = unserialize($instance['products']); } // form ids @@ -203,19 +303,28 @@ function widget($args, $instance){ $this->subscriptions = new paymill_subscriptions('pay_button'); } $offers = $this->subscriptions->offerGetList(); - + // html / icons - echo '
'; + echo ' +
+ + '; if(file_exists(get_template_directory().'/paymill/pay_button.php')){ require(get_template_directory().'/paymill/pay_button.php'); }else{ require(PAYMILL_DIR.'lib/tpl/pay_button.php'); } + echo '
'.__('Payment', 'paymill').'
'; + require(PAYMILL_DIR.'lib/tpl/checkout_form.php'); - echo ''; - echo '
'; + + echo ' + + +
+ '; } echo $args['after_widget']; @@ -250,8 +359,8 @@ function form($instance) { '; foreach($GLOBALS['paymill_settings']->paymill_pay_button_settings['products'] as $id => $product){ - if(strlen($product['title']) > 0){ - echo ''; + if(strlen($product['products_title']) > 0){ + echo ''; } } echo ' @@ -262,11 +371,9 @@ function form($instance) { '; } } - add_action('widgets_init', create_function('','register_widget("paymill_pay_button_widget");')); // creating shortcodes - // [sv_cb foo="foo-value"] function paymill_pay_button_shortcode($atts){ ob_start(); the_widget('paymill_pay_button_widget',$atts,$args); diff --git a/lib/integration/payment.inc.php b/lib/integration/payment.inc.php index 483f0c5..1ce95d7 100644 --- a/lib/integration/payment.inc.php +++ b/lib/integration/payment.inc.php @@ -5,30 +5,49 @@ class paymill_payment{ private $paymentData = false; + private $preauthData = false; + + public function __construct($client_id,$amount,$currency){ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_payment'); // benchmark + load_paymill(); // this function-call can and should be used whenever working with Paymill API - public function __construct($client_id){ if($this->paymentData === false){ - require_once(PAYMILL_DIR.'lib/api/Payments.php'); - - // create payment object for future transactions and/or subscriptions - $paymentsObject = new Services_Paymill_Payments( - $GLOBALS['paymill_settings']->paymill_general_settings['api_key_private'], - $GLOBALS['paymill_settings']->paymill_general_settings['api_endpoint'] - ); + try{ + $GLOBALS['paymill_loader']->request_payment->setToken($_POST['paymillToken']); + $GLOBALS['paymill_loader']->request_payment->setClient($client_id); - $paymentsParam = array( - 'token' => $_POST['paymillToken'], - 'client' => $client_id - ); - - $this->paymentData = $paymentsObject->create($paymentsParam); // Use this for other payment + $this->paymentData = $GLOBALS['paymill_loader']->request->create($GLOBALS['paymill_loader']->request_payment); // Use this for further payment processing + + if(is_object($this->paymentData) && $this->paymentData->getType() == 'creditcard'){ + $GLOBALS['paymill_loader']->request_preauth->setPayment($this->getPaymentID()); + $GLOBALS['paymill_loader']->request_preauth->setAmount(round($amount)); + $GLOBALS['paymill_loader']->request_preauth->setCurrency($currency); + + $this->preauthData = $GLOBALS['paymill_loader']->request->create($GLOBALS['paymill_loader']->request_preauth); // Use this for further payment processing, too + } + }catch(Exception $e){ + $GLOBALS['paymill_loader']->paymill_errors->setError(__($e->getMessage(),'paymill')); + } } + + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_payment'); // benchmark } public function getPaymentID(){ - return $this->paymentData['id']; + if(is_object($this->paymentData)){ + return $this->paymentData->getId(); + }else{ + return false; + } } + public function getPreauthID(){ + if(is_object($this->preauthData)){ + return $this->preauthData->getId(); + }else{ + return false; + } + } } ?> \ No newline at end of file diff --git a/lib/integration/shopplugin.inc.php b/lib/integration/shopplugin.inc.php index a76f396..e5c7809 100644 --- a/lib/integration/shopplugin.inc.php +++ b/lib/integration/shopplugin.inc.php @@ -1,4 +1,19 @@ '.$error.''; + } + + return $output; + } + } + + + class PaymillShopp extends GatewayFramework implements GatewayModule { // Settings @@ -6,18 +21,19 @@ class PaymillShopp extends GatewayFramework implements GatewayModule { var $saleonly = true; // force sale event on processing (no auth) var $recurring = false; // support for recurring payment - function __construct () { + function __construct(){ parent::__construct(); - + + $GLOBALS['paymill_loader']->paymill_errors->setFunction('paymill_shopp_errorHandling'); + if (!isset($this->settings['label'])){ - $this->settings['label'] = "Paymill"; + $this->settings['label'] = 'Paymill'; } add_action('shopp_paymillshopp_sale',array(&$this,'sale')); // Process sales $GLOBALS['paymill_source']['shopp_version'] = SHOPP_VERSION; } - /** * actions * @@ -31,17 +47,16 @@ function __construct () { **/ function actions () { //add_action('shopp_process_checkout', array($this,'process')); // intercept checkout request, force confirm - - add_filter( 'shopp_checkout_gateway_inputs', array($this,'form'),10,2); } - function form(){ if(!$GLOBALS['paymill_active']){ + paymill_load_frontend_scripts(); // load frontend scripts + // settings $GLOBALS['paymill_active'] = true; $cart_total = $this->amount('total')*100; - $currency = $GLOBALS['paymill_settings']->paymill_general_settings['currency']; + $currency = $this->currency(); $cc_logo = plugins_url('',__FILE__ ).'/../img/cc_logos_v.png'; $no_logos = true; @@ -54,9 +69,7 @@ function form(){ // html / icons echo '

'; - $icon = 'PAYMILL'; - if(isset($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']) && is_array($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']) && count($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']) > 0){ foreach($GLOBALS['paymill_settings']->paymill_general_settings['payments_display'] as $name => $type){ if($type==1){ @@ -64,103 +77,160 @@ function form(){ } } } - echo $icon; - echo '

'; - require_once(PAYMILL_DIR.'lib/tpl/checkout_form.php'); - echo '
'; - }else{ echo '
Error: Paymill can be loaded once only on the same page.
'; } } - - function sale ($Event) { + private function getCurrentClient(){ + require_once(PAYMILL_DIR.'lib/integration/client.inc.php'); + if(isset($this->order->Customer->firstname) && isset($this->order->Customer->lastname)){ + $desc = $this->order->Customer->firstname.' '.$this->order->Customer->lastname; + }elseif(isset($_POST['billing_first_name'])){ + $desc = $this->order->Customer->firstname; + }elseif(isset($_POST['billing_last_name'])){ + $desc = $this->order->Customer->lastname; + }else{ + $desc = ''; + } + + // create or get client + $this->clientClass = new paymill_client($this->order->Customer->email,$desc); + return $this->clientClass->getCurrentClient(); + }/* + private function getTotals(){ + $this->total = $this->Order->Cart->Totals; + $this->total_complete = $this->Order->Cart->Totals; + }*/ + private function processSubscriptions(){ + // subscriptions for shopp not yet supported. + return true; + } + private function processProducts(){ global $wpdb; + if($this->total > 0){ + // make transaction + $GLOBALS['paymill_loader']->request_transaction->setAmount(round($this->total)); // e.g. "4200" for 42.00 EUR + $GLOBALS['paymill_loader']->request_transaction->setCurrency($this->currency()); + if($this->paymentClass->getPreauthID() != false){ + $GLOBALS['paymill_loader']->request_transaction->setPreauthorization($this->paymentClass->getPreauthID()); + }else{ + $GLOBALS['paymill_loader']->request_transaction->setPayment($this->paymentClass->getPaymentID()); + } + $GLOBALS['paymill_loader']->request_transaction->setClient($this->client->getId()); + $GLOBALS['paymill_loader']->request_transaction->setDescription($this->order_desc); + $GLOBALS['paymill_loader']->request->setSource(serialize($GLOBALS['paymill_source'])); + + $GLOBALS['paymill_loader']->request->create($GLOBALS['paymill_loader']->request_transaction); - $order = $this->Order; - $orderTotals = $order->Cart->Totals; - $Billing = $order->Billing; - $Paymethod = $order->paymethod(); - - - // first retrieve client data, either from cache or from API - require_once(PAYMILL_DIR.'lib/integration/client.inc.php'); - $clientClass = new paymill_client( - $order->Customer->email, - $order->Customer->firstname.' '.$order->Customer->lastname - ); + $response = $GLOBALS['paymill_loader']->request->getLastResponse(); + + if(isset($response['body']['data']['response_code']) && $response['body']['data']['response_code'] != '20000'){ + $GLOBALS['paymill_loader']->paymill_errors->setError(__($response['body']['data']['response_code'], 'paymill')); + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + $GLOBALS['paymill_loader']->paymill_errors->getErrors(); + } + return false; + } + + // save data to transaction table + $wpdb->query($wpdb->prepare(' + INSERT INTO '.$wpdb->prefix.'paymill_transactions (paymill_transaction_id, paymill_payment_id, paymill_client_id, pay_button_order_id, paymill_transaction_time, paymill_transaction_data) + VALUES (%s,%s,%s,%d,%d,%s)', + array( + $response['body']['data']['id'], + $response['body']['data']['payment']['id'], + $response['body']['data']['client']['id'], + $this->order_id, + $this->order_id, + serialize($_POST) + ))); + + do_action('paymill_paybutton_products_paid', array( + 'total' => $this->total, + 'currency' => $this->currency(), + 'client' => $response['body']['data']['client']['id'] + )); + + return true; + }else{ // total is zero, so just return true + return true; + } + } + function sale ($Event) { + global $wpdb; - $client = $clientClass->getCurrentClient(); + $this->order_id = $Event->order; + $this->order_desc = __('Order #','paymill').$this->order_id; + $this->order = $this->Order; + $this->cart = $this->order->Cart; + $this->total_complete = round((floatval($Event->amount)*100)); + $this->total = $this->total_complete; + $Billing = $this->order->Billing; + $Paymethod = $this->order->paymethod(); + $this->client = $this->getCurrentClient(); + // client retrieved, now we are ready to process the payment - if($client['id'] !== false && strlen($client['id']) > 0){ + if($this->client->getId() !== false && strlen($this->client->getId()) > 0){ + // load subscription class + //$this->subscriptions = new paymill_subscriptions('shopp'); + //$this->offers = $this->subscriptions->offerGetList(); + + // get the totals for pre authorization + //$this->getTotals(); + + // create payment object and preauthorization require_once(PAYMILL_DIR.'lib/integration/payment.inc.php'); - - $paymentClass = new paymill_payment($client['id']); - - // calculate total based on product settings - $total = (floatval($orderTotals->total)*100); + $this->paymentClass = new paymill_payment($this->client->getId(),$this->total_complete,$this->currency()); // create payment object, as it should be used for next processing instead of the token. + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + shopp_add_order_event($Purchase->id, 'auth-fail', array( + 'amount' => $orderTotals->total, // Amount to be authorized + 'gateway' => $Event->gateway, // Gateway handler name (module name from @subpackage) + 'message' => $GLOBALS['paymill_loader']->paymill_errors->getErrors(), 'paymill') + ); + return false; + } - $order_id = $Event->order; - - // make transaction (single time) - if($total > 0){ - $transactionsObject = new Services_Paymill_Transactions($GLOBALS['paymill_settings']->paymill_general_settings['api_key_private'], $GLOBALS['paymill_settings']->paymill_general_settings['api_endpoint']); - - $ordermsg = __('Order', 'paymill').' #'.$order_id.' '.__('Forename', 'paymill').': '.strip_tags($order->Customer->firstname).' '.__('Surname', 'paymill').': '.strip_tags($order->Customer->lastname); - - $params = array( - 'amount' => str_replace('.','',"$total"), // e.g. "4200" for 42.00 EUR - 'currency' => $GLOBALS['paymill_settings']->paymill_general_settings['currency'], // ISO 4217 - 'payment' => $paymentClass->getPaymentID(), - 'client' => $client['id'], - 'description' => $ordermsg, - 'source' => serialize($GLOBALS['paymill_source']) - ); - $transaction = $transactionsObject->create($params); - - $response = $transactionsObject->getResponse(); - if(isset($response['body']['data']['response_code']) && $response['body']['data']['response_code'] != '20000'){ - shopp_add_order_event($Purchase->id, 'auth-fail', array( - 'amount' => $orderTotals->total, // Amount to be authorized - 'gateway' => $Event->gateway, // Gateway handler name (module name from @subpackage) - 'message' => $response['body']['data']['response_code'], 'paymill') - ); - }else{ - // save data to transaction table - $query = 'INSERT INTO '.$wpdb->prefix.'paymill_transactions (paymill_transaction_id, paymill_payment_id, paymill_client_id, shopplugin_order_id, paymill_transaction_time) VALUES ("'.$transaction['id'].'", "'.$transaction['payment']['id'].'", "'.$transaction['client']['id'].'", "'.$order_id.'", "'.time().'")'; - $wpdb->query($query); + // process subscriptions & products + if($this->processSubscriptions() && $this->processProducts()){ + // success + // the order_id usually comes from the auth or sale event object $Event->order + shopp_add_order_event( $Event->order, 'authed', array( + 'txnid' => $transaction['payment']['id'], // Transaction ID from payment gateway, in some cases will be in $Event->txnid + 'amount' => $orderTotals->total, // Gross amount authorized + 'gateway' => $Event->gateway, // Gateway handler name (module name from @subpackage) + 'paymethod' => $Paymethod->label, // Payment method (payment method label from your payment settings) + 'paytype' => $Billing->cardtype, // Type of payment (check, MasterCard, etc) + 'payid' => $Billing->card, // Payment ID (last 4 of card or check number) + )); + + // either immediately after authed (in the case of a sale event) + // or in response to a capture order event + shopp_add_order_event( $Event->order, 'captured', array( + 'txnid' => $transaction['payment']['id'], // Transaction ID from payment gateway, in some cases will be in $Event->txnid + 'amount' => $orderTotals->total, // Gross amount captured + 'gateway' => $Event->gateway, // Gateway handler name (module name from @subpackage) + 'fees' => 0 // Transaction fee assessed by the payment gateway + )); - // the order_id usually comes from the auth or sale event object $Event->order - shopp_add_order_event( $Event->order, 'authed', array( - 'txnid' => $transaction['payment']['id'], // Transaction ID from payment gateway, in some cases will be in $Event->txnid - 'amount' => $orderTotals->total, // Gross amount authorized - 'gateway' => $Event->gateway, // Gateway handler name (module name from @subpackage) - 'paymethod' => $Paymethod->label, // Payment method (payment method label from your payment settings) - 'paytype' => $Billing->cardtype, // Type of payment (check, MasterCard, etc) - 'payid' => $Billing->card, // Payment ID (last 4 of card or check number) - )); - - // either immediately after authed (in the case of a sale event) - // or in response to a capture order event - shopp_add_order_event( $Event->order, 'captured', array( - 'txnid' => $transaction['payment']['id'], // Transaction ID from payment gateway, in some cases will be in $Event->txnid - 'amount' => $orderTotals->total, // Gross amount captured - 'gateway' => $Event->gateway, // Gateway handler name (module name from @subpackage) - 'fees' => 0 // Transaction fee assessed by the payment gateway - )); - - do_action('paymill_shopplugin_products_paid', array( - 'total' => $total, - 'currency' => $GLOBALS['paymill_settings']->paymill_general_settings['currency'], - 'client' => $client['id'] - )); + do_action('paymill_shopplugin_products_paid', array( + 'total' => $total, + 'currency' => $this->currency(), + 'client' => $client['id'] + )); + }else{ + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + $GLOBALS['paymill_loader']->paymill_errors->getErrors(); } + return false; } + }else{ + $GLOBALS['paymill_loader']->paymill_errors->setError(__('There was an issue with adding you as client for the payment process.', 'paymill')); + return false; } } diff --git a/lib/integration/subscriptions.inc.php b/lib/integration/subscriptions.inc.php index 608cc8b..2b7bbfa 100644 --- a/lib/integration/subscriptions.inc.php +++ b/lib/integration/subscriptions.inc.php @@ -2,57 +2,62 @@ class paymill_subscriptions{ - var $store = false; - var $subscriptionsObject = false; - var $cache = false; + private $store = false; + private $subscriptionsObject = false; + private $cache = false; public function __construct($store){ - require_once(PAYMILL_DIR.'lib/api/Subscriptions.php'); - require_once(PAYMILL_DIR.'lib/api/Offers.php'); - require_once(PAYMILL_DIR.'lib/api/Preauthorizations.php'); - - $this->subscriptionsObject = new Services_Paymill_Subscriptions($GLOBALS['paymill_settings']->paymill_general_settings['api_key_private'], $GLOBALS['paymill_settings']->paymill_general_settings['api_endpoint']); - $this->offersObject = new Services_Paymill_Offers($GLOBALS['paymill_settings']->paymill_general_settings['api_key_private'], $GLOBALS['paymill_settings']->paymill_general_settings['api_endpoint']); - $this->preAuthObject = new Services_Paymill_Preauthorizations($GLOBALS['paymill_settings']->paymill_general_settings['api_key_private'], $GLOBALS['paymill_settings']->paymill_general_settings['api_endpoint']); - $this->store = $store; } public function getList(){ - return $this->subscriptionsObject->get(); + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_subscription_getList'); // benchmark + load_paymill(); // this function-call can and should be used whenever working with Paymill API + + try{ + $output = $request->getAll($GLOBALS['paymill_loader']->request_subscription); + }catch(Exception $e){ + $GLOBALS['paymill_loader']->paymill_errors->setError(__($e->getMessage(),'paymill')); + $output = false; + } + + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_subscription_getList'); // benchmark + return $output; } public function details($sub_id){ - return $this->subscriptionsObject->getOne($sub_id); - } - public function create($client, $offer, $payment){ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_subscription_details'); // benchmark + load_paymill(); // this function-call can and should be used whenever working with Paymill API - // retrieve offer amount for preauthorization - $offerDetail = $this->offerGetDetailByID($offer); - - // make preauth - $params = array( - 'payment' => $payment, - 'amount' => $offerDetail[0]['amount'], - 'currency' => $offerDetail[0]['currency'] - ); + try{ + $GLOBALS['paymill_loader']->request_subscription->setId($sub_id); + $output = $GLOBALS['paymill_loader']->request->getOne($GLOBALS['paymill_loader']->request_subscription); + }catch(Exception $e){ + $GLOBALS['paymill_loader']->paymill_errors->setError(__($e->getMessage(),'paymill')); + $output = false; + } - $preAuthResponse = $this->preAuthObject->create($params); + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_subscription_details'); // benchmark + return $output; + } + public function create($client, $offer, $payment){ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_subscription_create'); // benchmark + load_paymill(); // this function-call can and should be used whenever working with Paymill API - if($preAuthResponse['preauthorization']['status'] == 'failed'){ - echo __($preAuthResponse['response_code'],'paymill'); - die(); - }else{ - $params = array( - 'client' => $client, - 'offer' => $offer, - 'payment' => $payment - ); + try{ + $GLOBALS['paymill_loader']->request_subscription->setClient($client); + $GLOBALS['paymill_loader']->request_subscription->setOffer($offer); + $GLOBALS['paymill_loader']->request_subscription->setPayment($payment); - $subscription = $this->subscriptionsObject->create($params); - - return $subscription; + $subscription = $GLOBALS['paymill_loader']->request->create($GLOBALS['paymill_loader']->request_subscription); + $output = $subscription->getId(); + }catch(Exception $e){ + $GLOBALS['paymill_loader']->paymill_errors->setError(__($e->getMessage(),'paymill')); + $output = false; } - }/* + + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_subscription_create'); // benchmark + return $output; + }/* @todo public function update(){ $params = array( 'id' => '', @@ -62,60 +67,134 @@ public function update(){ ); $subscription = $this->subscriptionsObject->update($params); }*/ - public function remove($subscriptionid){ - $this->subscriptionsObject->delete($subscriptionid); + public function remove($sub_id){ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_subscription_remove'); // benchmark + load_paymill(); // this function-call can and should be used whenever working with Paymill API + + try{ + $GLOBALS['paymill_loader']->request_subscription->setId($sub_id); + + $response = $GLOBALS['paymill_loader']->request->delete($GLOBALS['paymill_loader']->request_subscription); + $output = $response; + }catch(Exception $e){ + $GLOBALS['paymill_loader']->paymill_errors->setError(__($e->getMessage(),'paymill')); + $output = false; + } + + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_subscription_remove'); // benchmark + return $output; } public function offerGetList($reCache=false){ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_subscription_offerGetList'); // benchmark global $wpdb; - - if($reCache === true){ - $offersList = $this->offersObject->get(); - foreach($offersList as $offer){ - $offersListSorted[$offer['id']] = $offer; - } - $query = "REPLACE INTO ".$wpdb->prefix."paymill_cache SET cache_id='subscription_plans',cache_content='".$wpdb->escape(serialize($offersListSorted))."'"; - $wpdb->query($query); - - $this->cache['subscription_plans'] = $offersListSorted; + try{ + if($reCache === true){ + load_paymill(); // this function-call can and should be used whenever working with Paymill API + $wpdb->query('TRUNCATE TABLE '.$wpdb->prefix.'paymill_cache_offers'); + + $offersList = $GLOBALS['paymill_loader']->request->getAll($GLOBALS['paymill_loader']->request_offer); - return $offersListSorted; - }elseif(is_array($this->cache['subscription_plans'])){ - return $this->cache['subscription_plans']; - }else{ - $query = 'SELECT * FROM '.$wpdb->prefix.'paymill_cache WHERE cache_id="subscription_plans"'; - $offersList = $wpdb->get_results($query,ARRAY_A); - - $offersList = unserialize($offersList[0]['cache_content']); - $this->cache['subscription_plans'] = $offersList; - return $offersList; + foreach($offersList as $offer){ + $wpdb->insert( + $wpdb->prefix.'paymill_cache_offers', + array( + 'id' => $offer['id'], + 'name' => $offer['name'], + 'amount' => $offer['amount'], + 'currency' => $offer['currency'], + 'interval' => $offer['interval'], + 'trial_period_days' => $offer['trial_period_days'] + ), + array( + '%s', + '%s', + '%d', + '%s', + '%s', + '%d' + ) + ); + $this->cache['subscription_plans'][$offer['id']] = $offer; + $this->cache['subscription_plans_by_name'][$offer['name']] = $offer; + } + }elseif(empty($this->cache['subscription_plans'])){ + $query = 'SELECT * FROM '.$wpdb->prefix.'paymill_cache_offers'; + $offersList = $wpdb->get_results($query,ARRAY_A); + foreach($offersList as $offer){ + $this->cache['subscription_plans'][$offer['id']] = $offer; + $this->cache['subscription_plans_by_name'][$offer['name']] = $offer; + } + }else{ + $output = false; + } + }catch(Exception $e){ + $GLOBALS['paymill_loader']->paymill_errors->setError(__($e->getMessage(),'paymill')); + $output = false; } + + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_subscription_offerGetList'); // benchmark + return $this->cache['subscription_plans']; } - /* - public function offerGetDetail($reCache=false){ - if(!$reCache && isset($this->cache[$offer_id]) && is_array($this->cache[$offer_id])){ - return $this->cache[$offer_id]; + public function offerGetDetailByID($id){ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_subscription_offerGetDetailByID'); // benchmark + load_paymill(); // this function-call can and should be used whenever working with Paymill API + + if(isset($this->cache['subscription_plans'][$id]) && is_array($this->cache['subscription_plans'][$id]) && count($this->cache['subscription_plans'][$id]) > 0){ + $output = $this->cache['subscription_plans'][$id]; }else{ - $this->offerGetList($reCache); - return (isset($this->cache[$offer_id]) ? $this->cache[$offer_id] : false); + $this->offerGetList(true); + if(isset($this->cache['subscription_plans'][$id]) && is_array($this->cache['subscription_plans'][$id]) && count($this->cache['subscription_plans'][$id]) > 0){ + $output = $this->cache['subscription_plans'][$id]; + }else{ + $output = false; + } } - }*/ - public function offerGetDetailByID($id){ - return $this->offersObject->get($id); + + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_subscription_offerGetDetailByID'); // benchmark + return $output; } public function offerGetDetailByName($name){ - return $this->offersObject->get(array('name' => $name)); + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_subscription_offerGetDetailByName'); // benchmark + load_paymill(); // this function-call can and should be used whenever working with Paymill API + + if(isset($this->cache['subscription_plans_by_name'][$name]) && is_array($this->cache['subscription_plans_by_name'][$name]) && count($this->cache['subscription_plans_by_name'][$name]) > 0){ + $output = $this->cache['subscription_plans_by_name'][$name]; + }else{ + $this->offerGetList(true); + if(isset($this->cache['subscription_plans_by_name'][$name]) && is_array($this->cache['subscription_plans_by_name'][$name]) && count($this->cache['subscription_plans_by_name'][$name]) > 0){ + $output = $this->cache['subscription_plans_by_name'][$name]; + }else{ + $output = false; + } + } + + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_subscription_offerGetDetailByName'); // benchmark + return $output; } public function offerCreate($params){ - /*$params = array( - 'amount' => '4200', // E.g. "4200" for 42.00 EUR - 'currency' => 'EUR', // ISO 4217 - 'interval' => '1 MONTH', - 'name' => 'Test Offer' - );*/ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_subscription_offerCreate'); // benchmark + load_paymill(); // this function-call can and should be used whenever working with Paymill API + + try{ + $GLOBALS['paymill_loader']->request_offer->setAmount(round($params['amount'])); + $GLOBALS['paymill_loader']->request_offer->setCurrency($params['currency']); + $GLOBALS['paymill_loader']->request_offer->setInterval($params['interval']); + $GLOBALS['paymill_loader']->request_offer->setName($params['name']); + $GLOBALS['paymill_loader']->request_offer->setTrialPeriodDays($params['trial_period_days']); + + $output = $GLOBALS['paymill_loader']->request->create($GLOBALS['paymill_loader']->request_offer); + $offerID = $output->getId(); + $this->offerGetList(true); + $output = $this->offerGetDetailByID($offerID); + }catch(Exception $e){ + $GLOBALS['paymill_loader']->paymill_errors->setError(__($e->getMessage(),'paymill')); + $output = false; + } - return $this->offersObject->create($params); + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_subscription_offerCreate'); // benchmark + return $output; } } \ No newline at end of file diff --git a/lib/integration/woocommerce.inc.php b/lib/integration/woocommerce.inc.php index 0e45218..973e74b 100644 --- a/lib/integration/woocommerce.inc.php +++ b/lib/integration/woocommerce.inc.php @@ -1,76 +1,366 @@ '.$error.''; + } + + $woocommerce->add_error($output); + } } - add_filter( 'woocommerce_payment_gateways', 'add_paymill_gateway_class' ); + // HOOKED FUNCTIONS FROM PAYMILL WEBHOOKS + function paymill_webhooks(){ + global $wpdb; + + // is there a webhook from Paymill? + if(class_exists('WC_Subscriptions_Manager')){ + + // grab data from webhook + $body = @file_get_contents('php://input'); + $event_json = json_decode($body, true); + + // retrieve sub ID + if(isset($event_json['event']['event_resource']['id']) && strlen($event_json['event']['event_resource']['id']) > 0){ + $paymill_sub_id = $event_json['event']['event_resource']['id']; + }elseif(isset($event_json['event']['event_resource']['subscription']['id']) && strlen($event_json['event']['event_resource']['subscription']['id']) > 0){ + $paymill_sub_id = $event_json['event']['event_resource']['subscription']['id']; + } + + error_log("\n\n########################################################################################################################\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); + error_log(date(DATE_RFC822).' - Webhook '.$event_json['event']['event_type'].' (Resource-ID: '.$paymill_sub_id.') triggered - start processing'."\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); + + /* output example: + array(1) { + ["event"]=> + array(4) { + ["event_type"]=> + string(20) "subscription.deleted" + ["event_resource"]=> + array(13) { + ["id"]=> + string(24) "sub_b71adbf5....." + ["offer"]=> + array(10) { + ["id"]=> + string(26) "offer_8083a5b....." + ["name"]=> + string(39) "woo_91_73da6....." + ["amount"]=> + int(100) + ["currency"]=> + string(3) "EUR" + ["interval"]=> + string(5) "1 DAY" + ["trial_period_days"]=> + int(0) + ["created_at"]=> + int(1389547028) + ["updated_at"]=> + int(1389547028) + ["subscription_count"]=> + array(2) { + ["active"]=> + string(1) "1" + ["inactive"]=> + string(1) "1" + } + ["app_id"]=> + NULL + } + ["livemode"]=> + bool(false) + ["cancel_at_period_end"]=> + bool(false) + ["trial_start"]=> + NULL + ["trial_end"]=> + NULL + ["next_capture_at"]=> + int(1389836717) + ["created_at"]=> + int(1389663382) + ["updated_at"]=> + int(1389750317) + ["canceled_at"]=> + NULL + ["app_id"]=> + NULL + ["payment"]=> + array(12) { + ["id"]=> + string(28) "pay_4e3759f....." + ["type"]=> + string(10) "creditcard" + ["client"]=> + string(27) "client_dbe164....." + ["card_type"]=> + string(4) "visa" + ["country"]=> + NULL + ["expire_month"]=> + string(2) "12" + ["expire_year"]=> + string(4) "2020" + ["card_holder"]=> + string(13) "dfgdfgdfgdfgd" + ["last4"]=> + string(4) "1111" + ["created_at"]=> + int(1389663369) + ["updated_at"]=> + int(1389663380) + ["app_id"]=> + NULL + } + ["client"]=> + array(8) { + ["id"]=> + string(27) "client_dbe164....." + ["email"]=> + string(22) "matthias@pc-intern.com" + ["description"]=> + string(15) "Matthias Reuter" + ["created_at"]=> + int(1389547027) + ["updated_at"]=> + int(1389547027) + ["app_id"]=> + NULL + ["payment"]=> + array(2) { + [0]=> + array(12) { + ["id"]=> + string(28) "pay_1a5ff8....." + ["type"]=> + string(10) "creditcard" + ["client"]=> + string(27) "client_dbe16....." + ["card_type"]=> + string(4) "visa" + ["country"]=> + NULL + ["expire_month"]=> + string(2) "12" + ["expire_year"]=> + string(4) "2020" + ["card_holder"]=> + string(10) "dfgdfgdfgd" + ["last4"]=> + string(4) "1111" + ["created_at"]=> + int(1389547027) + ["updated_at"]=> + int(1389547028) + ["app_id"]=> + NULL + } + [1]=> + array(12) { + ["id"]=> + string(28) "pay_4e375....." + ["type"]=> + string(10) "creditcard" + ["client"]=> + string(27) "client_dbe164....." + ["card_type"]=> + string(4) "visa" + ["country"]=> + NULL + ["expire_month"]=> + string(2) "12" + ["expire_year"]=> + string(4) "2020" + ["card_holder"]=> + string(13) "dfgdfgdfgdfgd" + ["last4"]=> + string(4) "1111" + ["created_at"]=> + int(1389663369) + ["updated_at"]=> + int(1389663380) + ["app_id"]=> + NULL + } + } + ["subscription"]=> + array(2) { + [0]=> + string(24) "sub_fcc4....." + [1]=> + string(24) "sub_b71a....." + } + } + } + ["created_at"]=> + int(1389816435) + ["app_id"]=> + NULL + } + } + + */ + //error_log(var_export($event_json,true)."\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); + + // get subscription info, if available + if(isset($paymill_sub_id) && strlen($paymill_sub_id) > 0){ + + $sql = $wpdb->prepare('SELECT * FROM '.$wpdb->prefix.'paymill_subscriptions WHERE paymill_sub_id=%s', + array( + $paymill_sub_id + )); + + $sub_cache = $wpdb->get_results($sql,ARRAY_A); + $sub_cache = $sub_cache[0]; + + /* output example: + SELECT * FROM wp_paymill_subscriptions WHERE paymill_sub_id="sub_b71adbf5e097bbe5ba80" + */ + error_log("\n\n".$sql."\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); + + /* output example: + + 1 + + 30 + + */ + //error_log($sub_cache['woo_user_id']."\n\n".$sub_cache['woo_offer_id']."\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); + + $subscription = WC_Subscriptions_Manager::get_subscription($sub_cache['woo_offer_id']); + + // update subscriptions when webhook is triggered + if(isset($sub_cache['woo_offer_id']) && strlen($sub_cache['woo_offer_id']) > 0){ + // subscription successfully created + if($event_json['event']['event_type'] == 'subscription.created'){ + + } + // tell WooCommerce when payment for subscription is successfully processed + if($event_json['event']['event_type'] == 'subscription.succeeded'){ + /* example data WC_Subscriptions_Manager::get_subscription: + array(15) { + ["order_id"]=> + string(3) "201" + ["product_id"]=> + string(2) "91" + ["variation_id"]=> + string(0) "" + ["status"]=> + string(6) "active" + ["period"]=> + string(3) "day" + ["interval"]=> + string(1) "1" + ["length"]=> + string(2) "12" + ["start_date"]=> + string(19) "2014-01-12 17:17:10" + ["expiry_date"]=> + string(19) "2014-01-24 17:17:10" + ["end_date"]=> + string(1) "0" + ["trial_expiry_date"]=> + string(1) "0" + ["failed_payments"]=> + string(1) "0" + ["completed_payments"]=> + array(1) { + [0]=> + string(19) "2014-01-12 17:17:10" + } + ["suspension_count"]=> + string(1) "0" + ["last_payment_date"]=> + string(19) "2014-01-12 17:17:10" + } + */ + error_log(var_export($subscription,true)."\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); + + if(count($subscription['completed_payments']) >= 1){ + WC_Subscriptions_Manager::process_subscription_payments_on_order($subscription['order_id'], $subscription['product_id']); + }else{ + WC_Subscriptions_Manager::activate_subscriptions_for_order($subscription['order_id']); + $order = new WC_Order($subscription['order_id']); + $order->payment_complete(); + } + } + // cancel subscription, as it was deleted through Paymill dashboard + if($event_json['event']['event_type'] == 'subscription.deleted'){ + + $sql = $wpdb->prepare('DELETE FROM '.$wpdb->prefix.'paymill_subscriptions WHERE woo_user_id=%s AND woo_offer_id=%s', + array( + $sub_cache['woo_user_id'], + $sub_cache['woo_offer_id'] + )); + $wpdb->query($sql); + + error_log("\n\n".$sql."\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); + + //WC_Subscriptions_Manager::cancel_subscriptions_for_order( $order ); + WC_Subscriptions_Manager::cancel_subscription($sub_cache['woo_user_id'], $sub_cache['woo_offer_id']); + } + // tell WC that payment failure occured + if($event_json['event']['event_type'] == 'subscription.failed'){ + WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($subscription['order_id'], $subscription['product_id']); + } + } + } + error_log(date(DATE_RFC822).' - Webhook '.$event_json['event']['event_type'].' finished - end processing'."\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); + error_log("\n\n########################################################################################################################\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); + } + } + add_action('woocommerce_api_wc_gateway_paymill_gateway', 'paymill_webhooks'); - add_action( 'cancelled_subscription_paymill','woo_cancelled_subscription_paymill', 10, 2 ); - // add_action( 'updated_users_subscriptions','woo_updated_subscription_paymill', 10, 2 ); + add_action('cancelled_subscription_paymill','woo_cancelled_subscription_paymill', 10, 2); + //add_action( 'updated_users_subscriptions','woo_updated_subscription_paymill', 10, 2 ); //add_action( 'subscription_put_on-hold_paymill','woo_subscription_put_on_hold_paymill', 10, 2 ); //add_action( 'reactivated_subscription_paymill','woo_reactivated_subscription_paymill', 10, 2 ); - function woo_cancelled_subscription_paymill($user,$subscription_key){ global $wpdb; $userInfo = get_userdata(get_current_user_id()); - $subscriptions = new paymill_subscriptions('woocommerce'); - $query = 'SELECT paymill_sub_id FROM '.$wpdb->prefix.'paymill_subscriptions WHERE woo_user_id="'.$userInfo->ID.'" AND woo_offer_id="'.$user->id.'_'.$subscription_key.'"'; - $client_cache = $wpdb->get_results($query,ARRAY_A); - - $subscriptions->remove($client_cache[0]['paymill_sub_id']); + $client_cache = $wpdb->get_results($wpdb->prepare('SELECT paymill_sub_id FROM '.$wpdb->prefix.'paymill_subscriptions WHERE woo_user_id=%s AND woo_offer_id=%s',array($userInfo->ID,$user->id.'_'.$subscription_key)),ARRAY_A); - $query = 'DELETE FROM '.$wpdb->prefix.'paymill_subscriptions WHERE woo_user_id="'.$userInfo->ID.'" AND woo_offer_id="'.$user->id.'_'.$subscription_key.'"'; - $wpdb->query($query); + $this->subscriptions->remove($client_cache[0]['paymill_sub_id']); + $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.'paymill_subscriptions WHERE woo_user_id=%s AND woo_offer_id=%s',array($userInfo->ID,$user->id.'_'.$subscription_key))); } function woo_updated_subscription_paymill($user,$subscription_details){ - var_dump($user); - var_dump($subscription_details); - die(); + // @todo: implement support for changing/creating offer later } - function woo_subscription_put_on_hold_paymill(){ - + // currently not supported with Paymill } function woo_reactivated_subscription_paymill($user,$subscription_key){ - + // currently not supported with Paymill } - - function init_paymill_gateway_class() { + function add_paymill_gateway_class($methods){ + $methods[] = 'WC_Gateway_Paymill_Gateway'; + return $methods; + } + add_filter('woocommerce_payment_gateways', 'add_paymill_gateway_class'); + function init_paymill_gateway_class(){ global $wpdb; - - // update subscriptions when webhook is triggered - if(class_exists('WC_Subscriptions_Manager') && isset($_GET['paymill_webhook']) && $_GET['paymill_webhook'] == 1){ - $body = @file_get_contents('php://input'); - $event_json = json_decode($body, true); - - if($event_json['event']['event_type'] == 'subscription.deleted'){ - $query = 'SELECT * FROM '.$wpdb->prefix.'paymill_subscriptions WHERE paymill_sub_id="'.$event_json['event']['event_resource']['id'].'"'; - $sub_cache = $wpdb->get_results($query,ARRAY_A); - - $query = 'DELETE FROM '.$wpdb->prefix.'paymill_subscriptions WHERE woo_user_id="'.$sub_cache[0]['woo_user_id'].'" AND woo_offer_id="'.$sub_cache[0]['woo_offer_id'].'"'; - $wpdb->query($query); - - //error_log("\n\n".$query."\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); - //error_log($sub_cache[0]['woo_user_id']."\n\n".$sub_cache[0]['woo_offer_id']."\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); - - WC_Subscriptions_Manager::cancel_subscription($sub_cache[0]['woo_user_id'], $sub_cache[0]['woo_offer_id']); - } - } - + if(class_exists('WC_Payment_Gateway')){ class WC_Gateway_Paymill_Gateway extends WC_Payment_Gateway{ + + private $total = 0; + private $total_complete = 0; + private $cart = false; + private $order_id = false; + private $order = false; + private $subscriptions = false; + private $offers = false; + private $order_desc = ''; + public function __construct(){ - + load_paymill(); // this function-call can and should be used whenever working with Paymill API + $GLOBALS['paymill_loader']->paymill_errors->setFunction('paymill_woocommerce_errorHandling'); $GLOBALS['paymill_source']['woocommerce_version'] = ((isset($GLOBALS['woocommerce']) && is_object($GLOBALS['woocommerce']) && isset($GLOBALS['woocommerce']->version)) ? $GLOBALS['woocommerce']->version : 0); $this->id = 'paymill'; @@ -99,12 +389,10 @@ public function __construct(){ 'subscription_payment_method_change'*/ ); } - - // @todo: better icon - function get_icon() { + public function get_icon() { global $woocommerce; - $icon = '' . $this->title . ''; + $icon = '' . $this->title . ''; if(isset($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']) && is_array($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']) && count($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']) > 0){ foreach($GLOBALS['paymill_settings']->paymill_general_settings['payments_display'] as $name => $type){ @@ -114,91 +402,100 @@ function get_icon() { } } - return apply_filters( 'woocommerce_gateway_icon', $icon, $this->id ); + return apply_filters('woocommerce_gateway_icon', $icon, $this->id); } - public function init_form_fields(){ $this->form_fields = array( 'enabled' => array( - 'title' => __( 'Enable/Disable', 'woocommerce' ), - 'type' => 'checkbox', - 'label' => __( 'Enable PAYMILL Payment', 'woocommerce' ), - 'default' => 'yes' + 'title' => __('Enable/Disable', 'woocommerce'), + 'type' => 'checkbox', + 'label' => __('Enable PAYMILL Payment', 'woocommerce'), + 'default' => 'yes' ), 'title' => array( - 'title' => __( 'Title', 'woocommerce' ), - 'type' => 'text', - 'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ), - 'default' => __( 'PAYMILL Payment', 'woocommerce' ), - 'desc_tip' => true, + 'title' => __('Title', 'woocommerce'), + 'type' => 'text', + 'description' => __('This controls the title which the user sees during checkout.', 'woocommerce'), + 'default' => __('PAYMILL Payment', 'woocommerce'), + 'desc_tip' => true, ), 'description' => array( - 'title' => __( 'Customer Message', 'woocommerce' ), - 'type' => 'textarea', - 'default' => 'Payments made easy' + 'title' => __('Customer Message', 'woocommerce'), + 'type' => 'textarea', + 'default' => 'Payments made easy' ) ); } - - public function process_payment( $order_id ) { - global $woocommerce,$wpdb; - - // first retrieve client data, either from cache or from API + private function getCurrentClient(){ require_once(PAYMILL_DIR.'lib/integration/client.inc.php'); - $clientClass = new paymill_client( - $_POST['billing_email'], - $_POST['billing_first_name'].' '.$_POST['billing_last_name'] - ); + if(isset($_POST['billing_first_name']) && isset($_POST['billing_last_name'])){ + $desc = $_POST['billing_first_name'].' '.$_POST['billing_last_name']; + }elseif(isset($_POST['billing_first_name'])){ + $desc = $_POST['billing_first_name']; + }elseif(isset($_POST['billing_last_name'])){ + $desc = $_POST['billing_last_name']; + }else{ + $desc = ''; + } - $client = $clientClass->getCurrentClient(); - - // client retrieved, now we are ready to process the payment - if($client['id'] !== false && strlen($client['id']) > 0){ - require_once(PAYMILL_DIR.'lib/integration/payment.inc.php'); - - $paymentClass = new paymill_payment($client['id']); - - // calculate total based on product settings - $total = (floatval($woocommerce->cart->total)*100); - - $order = new WC_Order( $order_id ); - - // make subscription - if($client['id'] && $paymentClass->getPaymentID() && class_exists('WC_Subscriptions_Order') && WC_Subscriptions_Order::order_contains_subscription($order)){ - $cart = $woocommerce->cart->get_cart(); - - $subscriptions = new paymill_subscriptions('woocommerce'); - - foreach($cart as $product){ - - $woo_sub_key = WC_Subscriptions_Manager::get_subscription_key($order_id,$product['product_id']); - + // create or get client + $this->clientClass = new paymill_client($_POST['billing_email'],$desc); + return $this->clientClass->getCurrentClient(); + } + private function getTotals(){ + // retrieve subscriptions amount + if(class_exists('WC_Subscriptions_Order') && WC_Subscriptions_Order::order_contains_subscription($this->order)){ + foreach($this->cart as $product){ + if(is_object($product) && isset($product->id) && intval($product->id) > 0){ + $woo_sub_key = WC_Subscriptions_Manager::get_subscription_key($this->order_id,$product->id); + if(!WC_Subscriptions_Manager::user_has_subscription(get_current_user_id(), $woo_sub_key)){ + // retrieve subscription amount + $recurr_amount = floatval(floatval(WC_Subscriptions_Order::get_item_recurring_amount($this->order,$product->id))*100); + $this->total = $this->total-$recurr_amount; + if($this->total_complete == 0){ + $this->total_complete = $this->total_complete+$recurr_amount; + } + } + } + } + } + } + private function processSubscriptions(){ + global $wpdb; + + // check wether subscriptions addon is activated + if(class_exists('WC_Subscriptions_Order') && WC_Subscriptions_Order::order_contains_subscription($this->order)){ + $product = $this->cart; + //foreach($this->cart as $product){ + if(is_array($product) && isset($product['product_id']) && intval($product['product_id']) > 0){ + // product is a subscription? + $woo_sub_key = WC_Subscriptions_Manager::get_subscription_key($this->order_id,$product['product_id']); + // check wether user already has subscription + if(!WC_Subscriptions_Manager::user_has_subscription(get_current_user_id(), $woo_sub_key)){ + // required vars - $amount = (floatval(WC_Subscriptions_Order::get_item_recurring_amount( $order,$product['product_id'] ))*100); + $amount = (floatval(WC_Subscriptions_Order::get_item_recurring_amount($this->order,$product['product_id']))*100); $currency = get_woocommerce_currency(); - $interval = '1 '.strtoupper(WC_Subscriptions_Order::get_subscription_period( $order,$product['product_id'] )); - - // get trial time - $now = time(); - $trial_end = strtotime(WC_Subscriptions_Product::get_trial_expiration_date($product['product_id'], get_gmt_from_date($order->order_date))); + $interval = '1 '.strtoupper(WC_Subscriptions_Order::get_subscription_period($this->order,$product['product_id'])); + + $trial_end = strtotime(WC_Subscriptions_Product::get_trial_expiration_date($product['product_id'], get_gmt_from_date($this->order->order_date))); if($trial_end === false){ $trial_time = 0; }else{ - $datediff = $trial_end - $now; + $datediff = $trial_end - time(); $trial_time = ceil($datediff/(60*60*24)); } // md5 name $woo_sub_md5 = md5($amount.$currency.$interval.$trial_time); - + // get offer $name = 'woo_'.$product['product_id'].'_'.$woo_sub_md5; - $offer = $subscriptions->offerGetDetailByName($name); - $offer = $offer[0]; - + $offer = $this->subscriptions->offerGetDetailByName($name); + // check wether offer exists in paymill - if(count($offer) == 0){ + if($offer === false){ // offer does not exist in paymill yet, create it $params = array( 'amount' => $amount, @@ -207,125 +504,180 @@ public function process_payment( $order_id ) { 'name' => $name, 'trial_period_days' => intval($trial_time) ); - $offer = $subscriptions->offerCreate($params); - /* - ob_start(); - var_dump($product['product_id']); - var_dump(get_gmt_from_date($order->order_date)); - var_dump(WC_Subscriptions_Product::get_trial_expiration_date($product['product_id'], get_gmt_from_date($order->order_date))); - var_dump($now); - var_dump($trial_end); - var_dump($trial_time); - var_dump($offer); - $var = ob_get_flush(); - $woocommerce->add_error($var); - return;*/ - - if(isset($offer['error']['messages'])){ - foreach($offer['error']['messages'] as $field => $msg){ - $woocommerce->add_error($field.': '.$msg); - } - return; + $offer = $this->subscriptions->offerCreate($params); + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + return false; } } - + // create user subscription - $user_sub = $subscriptions->create($client['id'], $offer['id'], $paymentClass->getPaymentID()); - - //ob_start(); var_dump($offer);var_dump($offer['id']); $var = ob_get_flush(); - //$woocommerce->add_error($var); - - if(isset($user_sub['error']) && strlen($user_sub['error']) > 0){ - $woocommerce->add_error(__($user_sub['error'], 'paymill')); - return; + $user_sub = $this->subscriptions->create($this->client->getId(), $offer['id'], $this->paymentClass->getPaymentID()); + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + return false; }else{ - $query = 'INSERT INTO '.$wpdb->prefix.'paymill_subscriptions (paymill_sub_id, woo_user_id, woo_offer_id) VALUES ("'.$user_sub['id'].'", "'.$userInfo->ID.'", "'.$woo_sub_key.'")'; - $wpdb->query($query); + $wpdb->query($wpdb->prepare('INSERT INTO '.$wpdb->prefix.'paymill_subscriptions (paymill_sub_id, woo_user_id, woo_offer_id) VALUES (%s, %s, %s)', + array( + $user_sub, + get_current_user_id(), + $woo_sub_key + ))); // subscription successful do_action('paymill_woocommerce_subscription_created', array( - 'product_id' => $id, + 'product_id' => $product['product_id'], 'offer_id' => $offer['id'], 'offer_data' => $offer )); + + return true; } - + }else{ + // @todo: currently, WooCommerce does not support multiple subscriptions on checkout, so we can stop processing here if first subscription is already subscribed + $GLOBALS['paymill_loader']->paymill_errors->setError(__('Subscription already subscribed.', 'paymill')); + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + } + return false; } } + //} + }else{ + return true; + } + } + private function processProducts(){ + global $wpdb; + if($this->total > 0){ + // make transaction + $GLOBALS['paymill_loader']->request_transaction->setAmount(round($this->total)); // e.g. "4200" for 42.00 EUR + $GLOBALS['paymill_loader']->request_transaction->setCurrency(get_woocommerce_currency()); + if($this->paymentClass->getPreauthID() != false){ + $GLOBALS['paymill_loader']->request_transaction->setPreauthorization($this->paymentClass->getPreauthID()); + }else{ + $GLOBALS['paymill_loader']->request_transaction->setPayment($this->paymentClass->getPaymentID()); } + $GLOBALS['paymill_loader']->request_transaction->setClient($this->client->getId()); + $GLOBALS['paymill_loader']->request_transaction->setDescription($this->order_desc); + $GLOBALS['paymill_loader']->request->setSource(serialize($GLOBALS['paymill_source'])); - // make transaction (single time) - if($total > 0){ - $transactionsObject = new Services_Paymill_Transactions($GLOBALS['paymill_settings']->paymill_general_settings['api_key_private'], $GLOBALS['paymill_settings']->paymill_general_settings['api_endpoint']); + $GLOBALS['paymill_loader']->request->create($GLOBALS['paymill_loader']->request_transaction); - // make transaction - $params = array( - 'amount' => $total, // e.g. "4200" for 42.00 EUR - 'currency' => get_woocommerce_currency(), // ISO 4217 - 'payment' => $paymentClass->getPaymentID(), - 'client' => $client['id'], - 'description' => 'Order #'.$order_id, - 'source' => serialize($GLOBALS['paymill_source']) - ); - $transaction = $transactionsObject->create($params); - - $response = $transactionsObject->getResponse(); - if(isset($response['body']['data']['response_code']) && $response['body']['data']['response_code'] != '20000'){ - echo __($response['body']['data']['response_code'], 'paymill'); - die(); + $response = $GLOBALS['paymill_loader']->request->getLastResponse(); + + if(isset($response['body']['data']['response_code']) && $response['body']['data']['response_code'] != '20000'){ + $GLOBALS['paymill_loader']->paymill_errors->setError(__($response['body']['data']['response_code'], 'paymill')); + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + $GLOBALS['paymill_loader']->paymill_errors->getErrors(); } - - // save data to transaction table - $query = 'INSERT INTO '.$wpdb->prefix.'paymill_transactions (paymill_transaction_id, paymill_payment_id, paymill_client_id, woocommerce_order_id, paymill_transaction_data) VALUES ("'.$transaction['id'].'", "'.$transaction['payment']['id'].'", "'.$transaction['client']['id'].'", "'.$order_id.'", "'.$wpdb->escape(serialize($_POST)).'")'; - $wpdb->query($query); - - do_action('paymill_woocommerce_products_paid', array( - 'total' => $total, - 'currency' => $GLOBALS['paymill_settings']->paymill_general_settings['currency'], - 'client' => $client['id'] - )); + return false; } - $order->payment_complete(); - - // Reduce stock levels - $order->reduce_order_stock(); + // save data to transaction table + $wpdb->query($wpdb->prepare(' + INSERT INTO '.$wpdb->prefix.'paymill_transactions (paymill_transaction_id, paymill_payment_id, paymill_client_id, woocommerce_order_id, paymill_transaction_time, paymill_transaction_data) + VALUES (%s,%s,%s,%d,%d,%s)', + array( + $response['body']['data']['id'], + $response['body']['data']['payment']['id'], + $response['body']['data']['client']['id'], + $this->order_id, + time(), + serialize($_POST) + ))); + + do_action('paymill_woocommerce_products_paid', array( + 'total' => $this->total, + 'currency' => get_woocommerce_currency(), + 'client' => $response['body']['data']['client']['id'] + )); + + return true; + }else{ // total is zero, so just return true + return true; + } + } + public function process_payment($order_id){ + global $woocommerce,$wpdb; + + $this->client = $this->getCurrentClient(); + // client retrieved, now we are ready to process the payment + if($this->client->getId() !== false && strlen($this->client->getId()) > 0){ + $this->order_id = $order_id; + $this->order_desc = __('Order #','paymill').$this->order_id; + $this->order = new WC_Order($this->order_id); + $this->cart = reset($woocommerce->cart->get_cart()); + $this->total_complete = + $this->total = (floatval($this->order->get_total())*100); - // Remove cart - $woocommerce->cart->empty_cart(); + // load subscription class + $this->subscriptions = new paymill_subscriptions('woocommerce'); + $this->offers = $this->subscriptions->offerGetList(); + + // get the totals for pre authorization + $this->getTotals(); - // Return thankyou redirect - return array( - 'result' => 'success', - 'redirect' => $this->get_return_url( $order ) - ); + // create payment object and preauthorization + require_once(PAYMILL_DIR.'lib/integration/payment.inc.php'); + $this->paymentClass = new paymill_payment($this->client->getId(),$this->total_complete,get_woocommerce_currency()); // create payment object, as it should be used for next processing instead of the token. + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + $GLOBALS['paymill_loader']->paymill_errors->getErrors(); + return false; + } + // process subscriptions & products + if($this->processSubscriptions() && $this->processProducts()){ + // success + if(method_exists($order, 'payment_complete')){ + // if order contains subscription, mark payment complete later when webhook triggers succeeded payment + if(class_exists('WC_Subscriptions_Order') && WC_Subscriptions_Order::order_contains_subscription($this->order)){ + $this->order->update_status('on-hold', __( 'Awaiting payment confirmation from Paymill.', 'paymill' )); + }else{ + $this->order->payment_complete(); + } + } + + // Reduce stock levels + if(method_exists($order, 'reduce_order_stock')){ + $this->order->reduce_order_stock(); + } + + // Remove cart + $woocommerce->cart->empty_cart(); + + // Return thankyou redirect + return array( + 'result' => 'success', + 'redirect' => $this->get_return_url($this->order) + ); + }else{ + if($GLOBALS['paymill_loader']->paymill_errors->status()){ + $GLOBALS['paymill_loader']->paymill_errors->getErrors(); + } + return false; + } }else{ - $woocommerce->add_error(__($client['error'], 'paymill')); + $GLOBALS['paymill_loader']->paymill_errors->setError(__('There was an issue with adding you as client for the payment process.', 'paymill')); + return false; } } - public function validate_fields(){ global $woocommerce; // check Paymill payment if(empty($_POST['paymillToken'])){ - $woocommerce->add_error('Es konnte kein Token erstellt werden.'); - + $woocommerce->add_error(__('Token not Found','paymill')); return false; } - return true; } - public function payment_fields(){ global $woocommerce; if(!$GLOBALS['paymill_active']){ + paymill_load_frontend_scripts(); // load frontend scripts + // settings $GLOBALS['paymill_active'] = true; $cart_total = $woocommerce->cart->total*100; $currency = get_woocommerce_currency(); - $cc_logo = plugins_url('',__FILE__ ).'/../img/cc_logos_v.png'; $no_logos = true; // form ids @@ -344,4 +696,5 @@ public function payment_fields(){ } } } + add_action('plugins_loaded', 'init_paymill_gateway_class'); ?> \ No newline at end of file diff --git a/lib/js/jquery.dctooltip.1.0.js b/lib/js/jquery.dctooltip.1.0.js new file mode 100644 index 0000000..b72be45 --- /dev/null +++ b/lib/js/jquery.dctooltip.1.0.js @@ -0,0 +1,89 @@ +/* + + * DC Tooltip - jQuery tooltip plugin + * Copyright (c) 2011 Design Chemical + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ + +(function($){ + + //define the new for the plugin ans how to call it + $.fn.dcTooltip = function(options) { + + //set default options + var defaults = { + classWrapper : 'tooltip', + hoverDelay : 300, + speed : 'fast', + distance : 20, + padLeft : 0 + }; + + //call in the default otions + var options = $.extend(defaults, options); + + //act upon the element that is passed into the design + return this.each(function(options){ + + // 1. Get the text, create the tooltip and append + var getText = $(this).attr('title'); + var $wrapper = '
'+getText+'
'; + $(this).append($wrapper); + // 2. Get the dimensions of the tooltip + var $tooltip = $('.'+defaults.classWrapper,this); + var widthP = $tooltip.width(); + // 3. Get the dimensions of the element + var widthT = $(this).width(); + var heightT = $(this).height(); + // 4. Set margins based on element dimensions and distance for animation + var marginTop = heightT - defaults.distance; + var marginLeft = (widthP - widthT)/2; + marginLeft = -marginLeft + defaults.padLeft; + $tooltip.css({marginLeft: marginLeft+'px', bottom: marginTop+'px'}); + // 5. Set the element position to relative & tooltip opacity to 0 + $(this).css('position','relative'); + $tooltip.css('opacity',0); + // 6. Remove the element's title text + $(this).removeAttr('title'); + + // Configuration settings for HoverIntent plugin + var config = { + sensitivity: 2, // number = sensitivity threshold (must be 1 or higher) + interval: defaults.hoverDelay, // number = milliseconds for onMouseOver polling interval + over: linkOver, // function = onMouseOver callback (REQUIRED) + timeout: defaults.hoverDelay, // number = milliseconds delay before onMouseOut + out: linkOut // function = onMouseOut callback (REQUIRED) + }; + + // Initialise HoverIntent + $(this).hoverIntent(config); + + // Hover link over + function linkOver(){ + + $tooltip.show().css({ + bottom: marginTop+'px' + }).animate({ + bottom: defaults.distance+marginTop, + opacity: 1 + }, defaults.speed); + } + + // Hover link over + function linkOut(){ + + $('.'+defaults.classWrapper,this).animate({ + bottom: (defaults.distance*1.5)+marginTop, + opacity: 0 + }, defaults.speed, function() { + $(this).hide(); + }); + + } + }); + }; +})(jQuery); \ No newline at end of file diff --git a/lib/js/jquery.hoverIntent.minified.js b/lib/js/jquery.hoverIntent.minified.js new file mode 100644 index 0000000..de46e0e --- /dev/null +++ b/lib/js/jquery.hoverIntent.minified.js @@ -0,0 +1,9 @@ +/** +* hoverIntent r5 // 2007.03.27 // jQuery 1.1.2+ +* +* +* @param f onMouseOver function || An object with configuration options +* @param g onMouseOut function || Nothing (use configuration options object) +* @author Brian Cherne +*/ +(function($){$.fn.hoverIntent=function(f,g){var cfg={sensitivity:7,interval:100,timeout:0};cfg=$.extend(cfg,g?{over:f,out:g}:f);var cX,cY,pX,pY;var track=function(ev){cX=ev.pageX;cY=ev.pageY;};var compare=function(ev,ob){ob.hoverIntent_t=clearTimeout(ob.hoverIntent_t);if((Math.abs(pX-cX)+Math.abs(pY-cY))"); + form.submit(); } - - if (false == paymill.validateCvc(jQuery('#card-cvc').val())) { - jQuery(".paymill_payment_errors").text(paymill_lang.validateCvc); + if(paymill_shop_name == 'shopplugin'){ jQuery(paymill_form_checkout_submit_id).show(); - return false; } - - paymill.createToken({ - number:jQuery('.paymill_card-number').val(), - exp_month:jQuery('.paymill_card-expiry-month').val(), - exp_year:jQuery('.paymill_card-expiry-year').val(), - cvc:jQuery('.paymill_card-cvc').val(), - cardholder:jQuery('.paymill_holdername').val(), - amount_int:jQuery('.paymill_amount').val(), - currency:jQuery('.paymill_currency').val() - }, function (error, result) { - if (error) { - // Zeigt den Fehler überhalb des Formulars an - jQuery(".paymill_payment_errors").text(error.apierror); - - alert(error.apierror); - - jQuery(paymill_form_checkout_submit_id).show(); - } else { - jQuery(".paymill_payment_errors").text(""); - - var form = jQuery(paymill_form_checkout_id); - - // Token - var token = result.token; + }); + }else if(jQuery('#paymill_form_sepa').is(':visible')){ + if (false == paymill.validateIban(jQuery('#paymill_sepa_iban').val())) { + jQuery(".paymill_payment_errors").text(paymill_lang.validateIBAN); + jQuery(paymill_form_checkout_submit_id).show(); + return false; + } - //alert(token); - // Token in das Formular einfügen damit es an den Server übergeben wird - form.append(""); - - form.submit(); - } - //jQuery(paymill_form_checkout_submit_id).removeAttr("disabled"); - }); - }else if(jQuery('#form-elv').is(':visible')){ - if (false == paymill.validateAccountNumber(jQuery('#transaction-form-account').val())) { - jQuery(".paymill_payment_errors").text(paymill_lang.validateAccountNumber); + if (false == paymill.validateBic(jQuery('#paymill_sepa_bic').val())) { + jQuery(".paymill_payment_errors").text(paymill_lang.validateBIC); + jQuery(paymill_form_checkout_submit_id).show(); + return false; + } + + paymill.createToken({ + iban:jQuery('#paymill_sepa_iban').val(), + bic:jQuery('#paymill_sepa_bic').val(), + accountholder:jQuery('#paymill_holdername').val(), + amount_int:jQuery('.paymill_amount').val(), + currency:jQuery('.paymill_currency').val() + }, function (error, result) { + if (error) { + // shows error + alert(error.apierror); + jQuery(".paymill_payment_errors").text(error.apierror); jQuery(paymill_form_checkout_submit_id).show(); - return false; - } + } else { + jQuery(".paymill_payment_errors").text(""); + var form = jQuery(paymill_form_checkout_id); - if (false == paymill.validateBankCode(jQuery('#transaction-form-code').val())) { - jQuery(".paymill_payment_errors").text(paymill_lang.validateBankCode); - jQuery(paymill_form_checkout_submit_id).show(); - return false; + // insert token into form + var token = result.token; + form.append(""); + form.submit(); } - - paymill.createToken({ - number:jQuery('#transaction-form-account').val(), - bank:jQuery('#transaction-form-code').val(), - accountholder:jQuery('.paymill_holdername').val(), - amount_int:jQuery('.paymill_amount').val(), - currency:jQuery('.paymill_currency').val() - }, function (error, result) { - if (error) { - // Zeigt den Fehler überhalb des Formulars an - jQuery(".paymill_payment_errors").text(error.apierror); - - jQuery(paymill_form_checkout_submit_id).show(); - - alert(error.apierror); - } else { - jQuery(".paymill_payment_errors").text(""); - - var form = jQuery(paymill_form_checkout_id); - - // Token - var token = result.token; + }); + }else if(jQuery('#paymill_form_elv').is(':visible')){ + if (false == paymill.validateAccountNumber(jQuery('#paymill_elv_number').val())) { + jQuery(".paymill_payment_errors").text(paymill_lang.validateAccountNumber); + jQuery(paymill_form_checkout_submit_id).show(); + return false; + } - // Token in das Formular einfügen damit es an den Server übergeben wird - form.append(""); - - form.submit(); - } - jQuery(paymill_form_checkout_submit_id).removeAttr("disabled"); - }); + if (false == paymill.validateBankCode(jQuery('#paymill_elv_bank_code').val())) { + jQuery(".paymill_payment_errors").text(paymill_lang.validateBankCode); + jQuery(paymill_form_checkout_submit_id).show(); + return false; } - return false; + + paymill.createToken({ + number:jQuery('#paymill_elv_number').val(), + bank:jQuery('#paymill_elv_bank_code').val(), + accountholder:jQuery('#paymill_holdername').val(), + amount_int:jQuery('.paymill_amount').val(), + currency:jQuery('.paymill_currency').val() + }, function (error, result) { + if (error) { + // shows error + jQuery(".paymill_payment_errors").text(error.apierror); + jQuery(paymill_form_checkout_submit_id).show(); + } else { + jQuery(".paymill_payment_errors").text(""); + var form = jQuery(paymill_form_checkout_id); + + // insert token into form + var token = result.token; + form.append(""); + form.submit(); + } + }); + } + return false; } // display credit cart icons - jQuery('body').on('change', '#card-number', function() { - var cc_type = paymill.cardType(jQuery('#card-number').val()); + jQuery('body').on('change', '#paymill_card_number', function() { + var cc_type = paymill.cardType(jQuery('#paymill_card_number').val()); if(cc_type == 'Visa'){ - jQuery('#card-number').show().css("backgroundPosition","0 -210px"); + jQuery('#paymill_card_number').show().css("backgroundPosition","0 -240px"); }else if(cc_type == 'MasterCard'){ - jQuery('#card-number').show().css("backgroundPosition","0 -180px"); + jQuery('#paymill_card_number').show().css("backgroundPosition","0 -210px"); }else if(cc_type == 'American Express'){ - jQuery('#card-number').show().css("backgroundPosition","0 -150px"); + jQuery('#paymill_card_number').show().css("backgroundPosition","0 -180px"); }else if(cc_type == 'Diners Club'){ - jQuery('#card-number').show().css("backgroundPosition","0 -120px"); + jQuery('#paymill_card_number').show().css("backgroundPosition","0 -150px"); }else if(cc_type == 'Discover'){ - jQuery('#card-number').show().css("backgroundPosition","0 -90px"); + jQuery('#paymill_card_number').show().css("backgroundPosition","0 -120px"); }else if(cc_type == 'JCB'){ - jQuery('#card-number').show().css("backgroundPosition","0 -60px"); + jQuery('#paymill_card_number').show().css("backgroundPosition","0 -90px"); }else if(cc_type == 'Maestro'){ - jQuery('#card-number').show().css("backgroundPosition","0 -30px"); - }else if(cc_type == 'CuP'){ - jQuery('#card-number').show().css("backgroundPosition","0 0"); + jQuery('#paymill_card_number').show().css("backgroundPosition","0 -60px"); + }else if(cc_type == 'UnionPay'){ + jQuery('#paymill_card_number').show().css("backgroundPosition","0 -30px"); + }else if(cc_type == 'cb'){ + jQuery('#paymill_card_number').show().css("backgroundPosition","0 0"); }else{ - jQuery('#card-number').show().css("backgroundPosition","0 30px"); + jQuery('#paymill_card_number').show().css("backgroundPosition","0 30px"); } }); - - jQuery("body").on("change", "#billing-country", function() { - var country = jQuery('#billing-country').val(); - - if(country == 'DE'){ - jQuery('#form-switch-elv').show('slow'); - }else{ - jQuery('#form-switch-elv').hide('slow'); - - jQuery('#form-elv').hide('slow'); - jQuery('#form-switch-elv').removeClass('paymill_form-switch_active'); - - jQuery('#form-credit').show('slow'); - jQuery('#form-switch-credit').addClass('paymill_form-switch_active'); - } + + // Payment Type Form Switcher + jQuery('body').on('click', '#paymill_form_switch_credit', function() { + jQuery('#paymill_form_elv').hide('slow'); + jQuery('#paymill_form_switch_elv').removeClass('paymill_form_switch_active'); + jQuery('#paymill_form_sepa').hide('slow'); + jQuery('#paymill_form_switch_sepa').removeClass('paymill_form_switch_active'); + jQuery('#paymill_form_credit').show('slow'); + jQuery('#paymill_form_switch_credit').addClass('paymill_form_switch_active'); }); - - - // ELV switcher - jQuery('body').on('click', '#form-switch-elv', function() { - jQuery('#form-credit').hide('slow'); - jQuery('#form-switch-credit').removeClass('paymill_form-switch_active'); + jQuery('body').on('click', '#paymill_form_switch_sepa', function() { + jQuery('#paymill_form_credit').hide('slow'); + jQuery('#paymill_form_switch_credit').removeClass('paymill_form_switch_active'); + jQuery('#paymill_form_elv').hide('slow'); + jQuery('#paymill_form_switch_elv').removeClass('paymill_form_switch_active'); - jQuery('#form-elv').show('slow'); - jQuery('#form-switch-elv').addClass('paymill_form-switch_active'); + jQuery('#paymill_form_sepa').show('slow'); + jQuery('#paymill_form_switch_sepa').addClass('paymill_form_switch_active'); }); - jQuery('body').on('click', '#form-switch-credit', function() { - jQuery('#form-elv').hide('slow'); - jQuery('#form-switch-elv').removeClass('paymill_form-switch_active'); + jQuery('body').on('click', '#paymill_form_switch_elv', function() { + jQuery('#paymill_form_credit').hide('slow'); + jQuery('#paymill_form_switch_credit').removeClass('paymill_form_switch_active'); + jQuery('#paymill_form_sepa').hide('slow'); + jQuery('#paymill_form_switch_sepa').removeClass('paymill_form_switch_active'); - jQuery('#form-credit').show('slow'); - jQuery('#form-switch-credit').addClass('paymill_form-switch_active'); + jQuery('#paymill_form_elv').show('slow'); + jQuery('#paymill_form_switch_elv').addClass('paymill_form_switch_active'); }); // Paymill Pay Button diff --git a/lib/js/paymill_admin.js b/lib/js/paymill_admin.js index 3a2ba61..6135a39 100644 --- a/lib/js/paymill_admin.js +++ b/lib/js/paymill_admin.js @@ -20,5 +20,6 @@ jQuery(document).ready(function () { }); return false; }); - + + jQuery('.paymill_settings_section_pay_button_products tr:first-child td, .paymill_settings_section_pay_button_shipping tr:first-child td').dcTooltip({distance: 0,padLeft: 0}); }); \ No newline at end of file diff --git a/lib/loader.inc.php b/lib/loader.inc.php new file mode 100644 index 0000000..989b47a --- /dev/null +++ b/lib/loader.inc.php @@ -0,0 +1,107 @@ +load_class_file($class); + }); + + // there may be more elegant ways for autoloading, but I'm new to namespaces and I'm rare of time atm. + // @todo: review for better way of autoloading Paymill API classes. + class paymill_loader{ + public $translation = array( + // for use in plugin + 'request' => array('lib/api/lib/Paymill/Request.php', 'Paymill\Request'), + 'request_client' => array('lib/api/lib/Paymill/Models/Request/Client.php', 'Paymill\Models\Request\Client'), + 'request_offer' => array('lib/api/lib/Paymill/Models/Request/Offer.php', 'Paymill\Models\Request\Offer'), + 'request_payment' => array('lib/api/lib/Paymill/Models/Request/Payment.php', 'Paymill\Models\Request\Payment'), + 'request_preauth' => array('lib/api/lib/Paymill/Models/Request/Preauthorization.php', 'Paymill\Models\Request\Preauthorization'), + 'request_refund' => array('lib/api/lib/Paymill/Models/Request/Refund.php', 'Paymill\Models\Request\Refund'), + 'request_subscription' => array('lib/api/lib/Paymill/Models/Request/Subscription.php', 'Paymill\Models\Request\Subscription'), + 'request_transaction' => array('lib/api/lib/Paymill/Models/Request/Transaction.php', 'Paymill\Models\Request\Transaction'), + 'request_webhook' => array('lib/api/lib/Paymill/Models/Request/Webhook.php', 'Paymill\Models\Request\Webhook'), + 'response_base' => array('lib/api/lib/Paymill/Models/Response/Base.php'), + 'response_client' => array('lib/api/lib/Paymill/Models/Response/Client.php', 'Paymill\Models\Response\Client'), + 'response_error' => array('lib/api/lib/Paymill/Models/Response/Error.php', 'Paymill\Models\Response\Error'), + 'response_offer' => array('lib/api/lib/Paymill/Models/Response/Offer.php', 'Paymill\Models\Response\Offer'), + 'response_payment' => array('lib/api/lib/Paymill/Models/Response/Payment.php', 'Paymill\Models\Response\Payment'), + 'response_preauth' => array('lib/api/lib/Paymill/Models/Response/Preauthorization.php', 'Paymill\Models\Response\Preauthorization'), + 'response_refund' => array('lib/api/lib/Paymill/Models/Response/Refund.php', 'Paymill\Models\Response\Refund'), + 'response_subscription' => array('lib/api/lib/Paymill/Models/Response/Subscription.php', 'Paymill\Models\Response\Subscription'), + 'response_transaction' => array('lib/api/lib/Paymill/Models/Response/Transaction.php', 'Paymill\Models\Response\Transaction'), + 'response_webhook' => array('lib/api/lib/Paymill/Models/Response/Webhook.php', 'Paymill\Models\Response\Webhook'), + + // for use within API + 'Paymill\Models\Response\Base' => array('lib/api/lib/Paymill/Models/Response/Base.php'), + 'Paymill\Models\Response\Client' => array('lib/api/lib/Paymill/Models/Response/Client.php', 'Paymill\Models\Response\Client'), + 'Paymill\Models\Response\Error' => array('lib/api/lib/Paymill/Models/Response/Error.php', 'Paymill\Models\Response\Error'), + 'Paymill\Models\Response\Offer' => array('lib/api/lib/Paymill/Models/Response/Offer.php', 'Paymill\Models\Response\Offer'), + 'Paymill\Models\Response\Payment' => array('lib/api/lib/Paymill/Models/Response/Payment.php', 'Paymill\Models\Response\Payment'), + 'Paymill\Models\Response\Preauthorization' => array('lib/api/lib/Paymill/Models/Response/Preauthorization.php', 'Paymill\Models\Response\Preauthorization'), + 'Paymill\Models\Response\Refund' => array('lib/api/lib/Paymill/Models/Response/Refund.php', 'Paymill\Models\Response\Refund'), + 'Paymill\Models\Response\Subscription' => array('lib/api/lib/Paymill/Models/Response/Subscription.php', 'Paymill\Models\Response\Subscription'), + 'Paymill\Models\Response\Transaction' => array('lib/api/lib/Paymill/Models/Response/Transaction.php', 'Paymill\Models\Response\Transaction'), + 'Paymill\Models\Response\Webhook' => array('lib/api/lib/Paymill/Models/Response/Webhook.php', 'Paymill\Models\Response\Webhook'), + + // paymill plugin classes + 'paymill_errors' => array('lib/errors.inc.php', 'paymill_errors') + ); + + public function __construct(){ + // make sure to load some dependencies + require_once(PAYMILL_DIR.'lib/api/lib/Paymill/API/CommunicationAbstract.php'); + require_once(PAYMILL_DIR.'lib/api/lib/Paymill/API/Curl.php'); + require_once(PAYMILL_DIR.'lib/api/lib/Paymill/Models/Request/Base.php'); + require_once(PAYMILL_DIR.'lib/api/lib/Paymill/Models/Response/Error.php'); + require_once(PAYMILL_DIR.'lib/api/lib/Paymill/Services/PaymillException.php'); + require_once(PAYMILL_DIR.'lib/api/lib/Paymill/Services/ResponseHandler.php'); + } + + public function __get($name){ + return $this->load_class($name); + } + + public function load_class($name){ + if($this->load_class_file($name)){ + return $this->init_class($name); + } + } + public function load_class_file($name){ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_API_'.$name.'_loaded'); // benchmark + // requested class in list? + if(isset($this->translation[$name]) && is_array($this->translation[$name]) && count($this->translation[$name]) > 0){ + // class file exists? + if(file_exists(PAYMILL_DIR.$this->translation[$name][0])){ + // load file + require_once(PAYMILL_DIR.$this->translation[$name][0]); + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_API_'.$name.'_loaded'); // benchmark + return true; + }else{ + return false; + } + } + } + public function init_class($name){ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_API_'.$name.'_initialized'); // benchmark + // default parameters + $default_param = array( + 'request' => (isset($GLOBALS['paymill_settings']->paymill_general_settings['api_key_private']) ? $GLOBALS['paymill_settings']->paymill_general_settings['api_key_private'] : false) + ); + + // init class? + if(isset($this->translation[$name][1]) && strlen($this->translation[$name][1]) > 0){ + // parameter for init given? + if(isset($default_param[$name])){ + $this->$name = new $this->translation[$name][1]($default_param[$name]); + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_API_'.$name.'_initialized'); // benchmark + return $this->$name; + // no parameter given for init + }else{ + $this->$name = new $this->translation[$name][1](); + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_API_'.$name.'_initialized'); // benchmark + return $this->$name; + } + } + } + } +?> \ No newline at end of file diff --git a/lib/scripts.inc.php b/lib/scripts.inc.php new file mode 100644 index 0000000..f546c1e --- /dev/null +++ b/lib/scripts.inc.php @@ -0,0 +1,75 @@ +setting_keys)){ + return; + } + + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_load_admin_scripts'); // benchmark + wp_enqueue_script('paymill_admin.js', PAYMILL_PLUGIN_URL.'lib/js/paymill_admin.js', array('jquery'), PAYMILL_VERSION); + wp_enqueue_script('jquery.dctooltip.1.0.js', PAYMILL_PLUGIN_URL.'lib/js/jquery.dctooltip.1.0.js', array('jquery'), PAYMILL_VERSION); + wp_enqueue_script('jquery.hoverIntent.minified.js', PAYMILL_PLUGIN_URL.'lib/js/jquery.hoverIntent.minified.js', array('jquery'), PAYMILL_VERSION); + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_load_admin_scripts'); // benchmark + } + + add_action('admin_init', 'paymill_load_admin_styles'); + function paymill_load_admin_styles() { + /* Register our stylesheet. */ + wp_register_style( 'paymill_admin.css', plugins_url('/css/paymill_admin.css', __FILE__) ); + } + + function paymill_admin_styles() { + /* + * It will be called only on your plugin admin page, enqueue our stylesheet here + */ + wp_enqueue_style( 'paymill_admin.css' ); + } + + // load frontend scripts + + // add this action when the payment form is viewed + // add_action('wp_enqueue_scripts', 'paymill_load_frontend_scripts'); + // or if above doesn't work, try this: + // paymill_load_frontend_scripts(); + + function paymill_load_frontend_scripts(){ + if(paymill_BENCHMARK)paymill_doBenchmark(true,'paymill_load_frontend_scripts'); // benchmark + wp_deregister_script(array('paymill_bridge','paymill_bridge_custom')); + wp_enqueue_script('jquery.formatCurrency-1.4.0.js',PAYMILL_PLUGIN_URL.'lib/js/jquery.formatCurrency-1.4.0.js', array('jquery'), PAYMILL_VERSION); + wp_enqueue_script('paymill_bridge', 'https://bridge.paymill.de/', array('jquery'), PAYMILL_VERSION); + wp_localize_script('paymill_bridge', 'paymill_lang', array( + 'validateCardNumber' => esc_attr__('Invalid Credit Card Number', 'paymill'), + 'validateExpiry' => esc_attr__('Invalid Expiration Date', 'paymill'), + 'validateCvc' => esc_attr__('Invalid CVC', 'paymill'), + 'validateAccountNumber' => esc_attr__('Invalid Account Number', 'paymill'), + 'validateBankCode' => esc_attr__('Invalid Bank Code', 'paymill'), + 'validateIBAN' => esc_attr__('Invalid IBAN', 'paymill'), + 'validateBIC' => esc_attr__('Invalid BIC', 'paymill'), + 'decimalSymbol' => esc_attr__($GLOBALS['paymill_settings']->paymill_pay_button_settings['number_decimal'], 'paymill'), + 'digitGroupSymbol' => esc_attr__($GLOBALS['paymill_settings']->paymill_pay_button_settings['number_thousands'], 'paymill'), + 'symbol' => esc_attr__($GLOBALS['paymill_settings']->paymill_pay_button_settings['currency'], 'paymill'), + )); + wp_enqueue_script('paymill_bridge_custom', PAYMILL_PLUGIN_URL.'lib/js/paymill.js', array('paymill_bridge'), PAYMILL_VERSION); + wp_enqueue_script('livevalidation', PAYMILL_PLUGIN_URL.'lib/js/livevalidation_standalone.compressed.js', array('paymill_bridge_custom'), PAYMILL_VERSION); + wp_enqueue_script('livevalidation_custom', PAYMILL_PLUGIN_URL.'lib/js/livevalidation_custom.js', array('livevalidation'), PAYMILL_VERSION); + wp_localize_script('livevalidation_custom', 'paymill_livevl', array( + 'wrongLength' => esc_attr__('Must be {is} characters long!', 'paymill'), + 'tooShort' => esc_attr__('Must not be less than {minimum} characters long!', 'paymill'), + 'tooLong' => esc_attr__('Must not be more than {maximum} characters long!', 'paymill'), + 'notANumber' => esc_attr__('Must be a number!', 'paymill'), + 'notAnInteger' => esc_attr__('Must be an integer!', 'paymill'), + 'wrongNumber' => esc_attr__('Must be {is}!', 'paymill'), + 'tooLow' => esc_attr__('Must not be less than {minimum}!', 'paymill'), + 'tooHigh' => esc_attr__('Must not be more than {maximum}!', 'paymill'), + 'notEmpty' => esc_attr__('Supply a value!', 'paymill'), + )); + + if(empty($GLOBALS['paymill_settings']->paymill_pay_button_settings['no_default_css']) || $GLOBALS['paymill_settings']->paymill_pay_button_settings['no_default_css'] != '1'){ + wp_enqueue_style('paymill', PAYMILL_PLUGIN_URL.'lib/css/paymill.css', false, PAYMILL_VERSION, false); + } + + if(paymill_BENCHMARK)paymill_doBenchmark(false,'paymill_load_frontend_scripts'); // benchmark + } +?> \ No newline at end of file diff --git a/lib/setup.inc.php b/lib/setup.inc.php new file mode 100644 index 0000000..b120d75 --- /dev/null +++ b/lib/setup.inc.php @@ -0,0 +1,95 @@ +request_webhook->setUrl(get_site_url().'/?wc-api=WC_Gateway_Paymill_Gateway'); + $GLOBALS['paymill_loader']->request_webhook->setEventTypes(array( + 'subscription.created', + 'subscription.deleted', + 'subscription.failed', + 'subscription.succeeded' + )); + $webhook = $GLOBALS['paymill_loader']->request->create($GLOBALS['paymill_loader']->request_webhook); + add_option('paymill_webhook_id', $webhook->getId()); + }else{ + $GLOBALS['paymill_loader']->request_webhook->setId(get_option('paymill_webhook_id')); + $GLOBALS['paymill_loader']->request_webhook->setUrl(get_site_url().'/?wc-api=WC_Gateway_Paymill_Gateway'); + $GLOBALS['paymill_loader']->request_webhook->setEventTypes(array( + 'subscription.created', + 'subscription.deleted', + 'subscription.failed', + 'subscription.succeeded' + )); + $webhook = $GLOBALS['paymill_loader']->request->update($GLOBALS['paymill_loader']->request_webhook); + update_option('paymill_webhook_id', $webhook->getId()); + } + }catch(Exception $e){ + echo __($e->getMessage(),'paymill'); + } + } + // install the tables + register_activation_hook(__FILE__,'paymill_install'); + function paymill_install(){ + load_paymill(); // this function-call can and should be used whenever working with Paymill API + global $wpdb; + + require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); + +$sql = 'CREATE TABLE '.$wpdb->prefix.'paymill_clients ( + paymill_client_id varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + paymill_client_email varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + paymill_client_description longtext CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + wp_member_id int(11) NOT NULL, + UNIQUE KEY paymill_client_id (paymill_client_id));'; + +$sql .= 'CREATE TABLE '.$wpdb->prefix.'paymill_transactions ( + paymill_transaction_id varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + paymill_payment_id varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + paymill_client_id varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + paymill_transaction_time int(11) NOT NULL, + paymill_transaction_data longtext CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + woocommerce_order_id int(11) NOT NULL, + pay_button_order_id int(11) NOT NULL, + shopplugin_order_id int(11) NOT NULL, + UNIQUE KEY paymill_transaction_id (paymill_transaction_id));'; + +$sql .= 'CREATE TABLE '.$wpdb->prefix.'paymill_cache_offers ( + `id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `name` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `amount` int(11) NOT NULL, + `currency` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `interval` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `trial_period_days` int(11) NOT NULL, + UNIQUE KEY id (id));'; + +$sql .= 'CREATE TABLE '.$wpdb->prefix.'paymill_subscriptions ( + paymill_sub_id varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + woo_user_id varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + woo_offer_id varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + mgm_user_id varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + mgm_offer_id varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + UNIQUE KEY paymill_sub_id (paymill_sub_id));'; + + dbDelta($sql); + + // get webhooks list + /*$GLOBALS['paymill_loader']->request_webhook->setId(get_option('paymill_webhook_id')); + $webhook = $GLOBALS['paymill_loader']->request->getOne($GLOBALS['paymill_loader']->request_webhook);*/ + + + + if(!get_option('paymill_db_version')){ + add_option('paymill_db_version', PAYMILL_VERSION); + }elseif(get_option('paymill_db_version') != PAYMILL_VERSION){ + update_option('paymill_db_version', PAYMILL_VERSION); + } + } + + if(!get_option('paymill_db_version')){ + paymill_install(); + }elseif(get_option('paymill_db_version') != PAYMILL_VERSION){ + paymill_install(); + } +?> \ No newline at end of file diff --git a/lib/tpl/checkout_form.php b/lib/tpl/checkout_form.php index 865466c..0249918 100644 --- a/lib/tpl/checkout_form.php +++ b/lib/tpl/checkout_form.php @@ -1,76 +1,75 @@
-
- paymill_general_settings['payments_display']) && is_array($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']) && count($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']) > 0){ -?> -
-

-paymill_general_settings['payments_display']) && is_array($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']) && count($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']) > 0){ + echo '

'; foreach($GLOBALS['paymill_settings']->paymill_general_settings['payments_display'] as $name => $type){ if($type==1){ - echo ''.$name.''; + if(!isset($no_logos)){ echo ''.$name.''; } + if($name == 'elv' || $name == 'sepa'){ + $count_bank_payment_types++; + } } + $count_all_payment_types++; } -?> -

-
- '; } - ?> - - paymill_general_settings['payments_display']['elv']) == 1) - && (count($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']) >= 2)) - ){ - ?> -
-
- $count_bank_payment_types){ + $show_cc = true; + echo '
'.__('Credit Card', 'paymill').'
'; + } + // SEPA + if(isset($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']['sepa']) && $GLOBALS['paymill_settings']->paymill_general_settings['payments_display']['sepa'] == 1){ + if(!isset($show_cc)){ + $show_sepa = true; + } + echo '
'.__('SEPA', 'paymill').'
'; + } + // ELV + if(isset($GLOBALS['paymill_settings']->paymill_general_settings['payments_display']['elv']) && $GLOBALS['paymill_settings']->paymill_general_settings['payments_display']['elv'] == 1){ + if(!isset($show_cc) && !isset($show_sepa)){ + $show_sepa = true; + } + echo '
'.__('ELV', 'paymill').'
'; } ?> -
- - +
+
-

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 diff --git a/lib/img/logos/cb.png b/lib/img/logos/cb.png new file mode 100644 index 0000000000000000000000000000000000000000..8ac69b0f0a84926890610ac48ddbfb87c76a3004 GIT binary patch literal 2954 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 P@^|cL=Sr%y2}t-if;9)5 literal 0 HcmV?d00001 diff --git a/lib/img/logos/elv.png b/lib/img/logos/elv.png index f0d55c1e5340b477b02eb6b33111d62d8fbf657d..bb18ae5fe82513b86a4790b5e70cddd03c0b4fc7 100644 GIT binary patch literal 3212 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ⅆL-hg%|~4xZoN1# z`9Ymg;OC^p<>l*VOts`a9sr?15xqeZ^NppSpKT($1|Ti--X4E`zx3YOu3%1h94>6f zVDlG&wvJKm&lQ0bz!AVIKm&k_{4y=JCJG!_Yj`Fo3z+YP4CwXiG<6?Q7t2iAsT4c_ zuhj`jFxWA@t(GUSP%p;1sABSQi5I1L6q=*!aeI+hSzS7she&ZqQQPGbmT#J!^=5N| zp7tVk*7wuRD5I;5)8X}#O(;v~&{i$)(Apb=k_;t$YWN⪼ zg8K_Jxig)9oF9)Rq(0Y~lg{owU{bdgJzq4e#_p0I)ajGBB&P-`i)M;A<-pSHxHY4|pi;aGH zF>rr&`g&vUDx&zKZg!^*Y0~2OXZ0XwWFL$kvI~}=YVYTfjk-AXYwels=;)g zetme%X87i~2(u?yu(+0j@J6}#$_}0!S|D>@c3OWpNWo@G-bk-n%BI*RtcDkV3@oge z`u2XBb?0Hr3Xa@|3_x3L`Zg!y_UildI81aOyE2Qp5(R0aeO&2kdwEP_&!Xsw2tQ8y zx?uaWxXYeX?0r`ijtIXU-@X%hZOb(X=<)WZL*9bVG63i7TA!%13YX|yt1AZnk~2Wr zm(jYhoH{WD%|`v}MD|`;Tk%82N1|p^pMSd%MDNYQZ>707-H(}<9>Sk^TCJ+H(nO~^zx7`##Yr`&YGu}Swb>od_`f`s{WnRwWzA^dqvy%}b z5*JezD>e&HX@Gfg<4k46qn94(d`^mpvtAu>@N!QN-$a)*)7Pe$a*Jf{oe3p*I@*S` zL?L(7nH~h9nR{O}m8W2s*db?m>ecg+j8uWW^U9{WncJ=@tH)pQrW1Zl-cW@=e%c1# zN6C&xCG;e6y~YbR$71H5o~cBfo=?-B7gyI^tP?z-E_6Pxd_}J&5WnE1q-q%OSObZ6 z|F~~+gPm4daM_M-BEfti`6@9*AU=%9EP;Pt-DpL_g`D|_ zkBV9BE5E=RwlD0SNL0UAeanMuwr$`e`b03D&MWWF+to~|iI(BwvXv_xN0qnA!_8#_ z?;I=jSy1q96iE>Dt5IUiRz!lE#@D554MGvmC)6y1l_TJRn9D6Jp9gJsVW&QhX4I~P z2YW%EBkf=JoZeOxEU@?C__^@hrdnMCOJ~MFPbgKSB@n5}5!Z2}J&Wl!O(#(mTMyW& z7jIR$677@bmMu4YwxeHH#4sGKqc8VE$CLFD`{o^6aYkyuU5TV9YA9!yiS^0qiy)Q2 zOH@@p&Bwc*DPiFj=Ds*8op$vmo5yU~lXkxSHN>f{nR2l9-k`YZr{;mw!jnLO%dh=& z^q1OlHtL54-LQ{?0#hH$%;hm4|iLO zTN-urdw4s2OOLi%;XQTAn9r0{H~{PX=;tx*Frr@dUNuoNohpBoQ!OY=6UgiJ3p!=x`6f> zMWTf=FKGq>@k8Dc`uQ&3WY=XplSAImbbaNRj;@$Rl6W0oo=kut0pIVwxY>6ouWyb> zNt>uE`((X+tW>?D;a)q9>%B^7Hhz%!H4w#V?fl}N9u{h9J}D$xm$5|G_@JXywAj!- zCih;pJtOU{F7duh_7OvwqdiNru|^T^&?z2-UnX-7wi@8j!t>kk(o`>DwObmprm zD+3iB>W$k!Wy((Mmt^5wZUS??JZ7fL;LD7v< zb)kFZ+vfmGaBkrG_6B5FkdvEWe6|v5(hh~=-afSwkLW3A@Bi3gM zVDZ_thR*v-_E*Vv%U?bjW0m#TB}U|yv2xaBw`q|6g|kr#(FB-i&MG7z zshzib#w(j0*QTI$N6QMV`lh(NTU4&_81RXe5qs{5deg(I^Q(IUY|b=ojyuKx4Hhi)?G>a9R);Wg&uRH#&r>t?;%v7*?(^C(4@jjKldioglmC#g6UF?7h~2eL49p4}!EjA$q&U zD?fe5ZVV*3p?;6bd{>vEMHZz&*;G1kcVRxk*dUzhA}0@79$&5(ak)4dr17J&?q}0p mZW@piIxev*V*N;n0N^vh-ib7j*17SoYi??VEk1lM^#1^kf9Fd8 literal 0 HcmV?d00001 diff --git a/lib/img/payment_logos.png b/lib/img/payment_logos.png new file mode 100644 index 0000000000000000000000000000000000000000..bc474e5e3de94f28f0e775c7d0b0a2b27cf05eea GIT binary patch literal 19704 zcmaI7W0)pEvn|@TZQHhO+qP}nHl}UcdfPqS)3$9)n|Ho__CDwSIQQ0{%FK!tu`(hm zD=MF=Xe9+nco-ZQARr)kX(=(4e`gjTAYcS2h<~l=JF&%oCu}!yEjLw13pY<=7jqzC zGe;A1B58YLOLG--V>55(Npn6RAW&j!H7z$Sc{v_aM|%e2|Hv?S**pD10|D^~dN~=J z+M2r&nV4HzJMfd-_6(2^S)1{bXtK*Q$vcUfTUkr_xR|T@D5#nG*qU;ikq8P9@p#()mb?;?sH2NH5jz7ry(tq5 zGZ7~@12a1tH#0LG5epMDDpYM9!5q_PfrFtl9r% z+W(E^`v1f-{v*TqpSb?N;`~2D|B~lF&HtUX|2qFW@68?lCA-VNtX8&7P5}avwwD$Y zR`c4p>Vx#d8+{Y}-0tZMxX^DlWA{9apfwr{0uBug27(SFLlS|t4g*6Gd5|R?5`qE? z8Dgdn+o_3%U&Cs2zSU<)S?;+hz3ll2ELiVb%#WeR9)Z23ESbA#pB0$n`^sL&t}bl;yI_i0=@8-|A5IA95=F(ED_Jd^`JAmTCMe8lNNuBo#4T1nR&tV%H{AzKdm7 z4FZWDc}S3C_SH>Y40t|_#Yi?zK3;PUL&}IZ&ArD(hBsrq`4ebfjfmj8nANF)+9N-A>{Thf!_Wrr3ySvn%}Bxc-k*XPggn3sxqhqeF7GRT1Oc;(YH|84S>?qbK+B!taT23gG-`xZn4oJ{x&N#hCW9_lg}9~L(gxpq!|rj#B#wjuFShZg`s=< zfjYa668Iq|=FugMp=P5WP1S%5Yn2p*&&D%8aKaUQYRi#Ic2l+*ec6P;dHDeY?e@r5 zsETeuZF|K-__x24@y4$;>QbSn(}4mUIWX|CQ&G&00biaM7)qU0hd+U2%v%7{Oz5qb zY={9GeqNLUeJwwt^qxH<_RMjMn-x@|p*g2YInI*?B52;04%&^|W5`4^4 z1K+}jNh+6nc-TxL>{;;D89HYfZH=Bngh~ikogUfLD&`fP4&_-iy*4VTz31soztZNs z*rz-EZ@z6LDpk32d)cYxT zQxmnxq&J-83@)9r4A%L6ib?0stB}cGTcDYVXz+Kx#d(RvMZ$>G?MW@tA2HHO0U2pz zb*tROjyV(?vJ~{zT% zSVo@{6aoDx99%(u9|w{VS?DW{9WUF9hYgDgyz8UOh@ z0NNIXI{AeR#ij{ch*CwoB4q&PHvOCG-sQ};>pvpU_#l#C5T9fjl%qb)eR zOBMe_IrhYPNENjGFq9ZD3-;dQr~u2k{|etd-i%Z=bgg?>u|!lIo8H?if(nTmqBTl{ z#Cc-d4D(0PAV9$gZ%hu^f?h)EO10~55(d~T3S7}NQc02nD<(<*3?d=ie`mlm)_2Ca zckWa>U>HF1IsF9i<3&*t$cXb{YbSQ2OC3Nr)=V{OS%1H8LmOE;k5G(P4gtm-kb@6~ z2P+(KesXfoH7fD~I@F4_?RppNxnD{rC3-YiS|E%Daz^98Rts>}r6n-bhNqvggnu1Q z{??wRNILtT^2aBRn=TQD+I==Lk>zcT`H4xCik;*z&98Q##I)a z(L8D975wvo%)yn0G{6wJ)xQj%+iGnKrgT$~9Ht8Z#| z8UjT5h3F3_iA`s`&a}kSpJ^yxxBGJ-hJZ78=b8EJTB#_dKPKu=M08SFU1dO@I#Pf_ zIsVwB28<#yavqCI*3)25_?N;;VHok_kM+P5do(8Nrb7cZ=7M0MtwnphLy~!eC^3?2 zXW(A4*Q7x9-x#v;)HI_JZd=lf9ZvnUNWqjR6rSgdEX+0Tko+;pCNQbMhn65?j;#v# zM&etkQDFvC%@HWOykkOJ#emtg!i^I;HYd{ZdKmN+@;pG#;cK z67av?;ECPIh)Cz(lI9q0lttpe+S<8=k}L3og^+UWZZ-35!knV~>9k{(R~vE?z<80$ zGNjLJp65csfW$rNV271!DGS`AK8l+L3V`Md;ZZkb&{SK7U?h0}{pApD#*l@G(7zsm z6j0s4plJ7jB0&6!kdfx@8B37r3NNoPk)^9ZRA)s*u;a8wWx;@Qr0>dX%tYiY5^ugz zwz9&RB&48`5L1<8A+SfPM}fpRb%$rCHNrv#CNL-uP>GCvs>EuG=NVTka%vytf(n{& z6*NJ=Q0_|kX4E1@-~lfpu+Me%+|l;!I`{5Wl5!@u*+0|$EI>?M`WC-$b5H$W?=90o z)AfgA#v_YAneOxD>eQ|k#t%sq$D6m5E7S8yXrjakhJ&8Hz$dZn0Jc?Rn)N`G+3Uvw z-QmPo!xWRr7EN|mnrW!0`q<*<$2eIFD){^Qz60ydt6zak;lms%oE(6xGTj0@z-dp4 ztJ7trqmk~4Q_P8)OqBBu3fNr;h>tjx)IS$k9g*a^2R5W+6=91XbzNT z2wxU~S!@bCo?$S55!ApKG$^{TI#M&FwWW^&U^H0vBcu*lS+xQ7w&t1f*qk)e!oYj`-yLUz2>s(l;#6o zlvG4!?7fv{p(;nZIJ{cAVp=p>RWCrb_JV5z4ygtEhz#0!@kCt&67t(mkf!5(Npn;= zEY~LJv+7bMMcN|*J_4W>n8>7~-9Sy_(E)^{o|f^_qK1r<50+pq_G(*Di~%2J8i0zl z(}ZiIk1D5q-YEa;5LCVKW;0gD69*x61AV@vT(!kBr&)5}u8Ta5tOgwX2Cb96I1{X{ zAIOH+AIeJ78eSezO%Fjgm$4X0v4k3;XJI4Zto$7`MMYb`wzbt_ZNM2}fv^knWDBp- z9>4K-UT|x8V5Ip4SQf92vaN?n1Fh^`ZMt94mKrbeycE4SC;YZl%nBkb=jT=txYtAN z;{DZn%iUF%rO{9}tFweM@E+m?9bWqMS%4d9LSxSFq9TWvZ;HTQ)Xa?=eVFs{I)dab zdw;wp_mk*HWPl3^lfTlF=qR)(^MUF{V6TMWyRu9RU#-JeI}{Itb2-`@KcsQti6Vw>eK*jmESTr)CE^%p=6ezbJO%PS@64 zDAS^!e_w5=_L;%;l{U5S4tPrzH=u8IvP*EPO88ipHuGrC&W7mmp~=YOFJ)*n(=%v| z#S1$vdPrqs54LgcEorwnm#Kwea;%bW+K={A5aQ(cbJ^N)FyyKQ`N!vb+33SB;IY*6 zH34zh#OXY=VqUF>Fk@S<$vpMDHo~;PME**~shvF)wRYf$Nl6u|gBK)bV`ao;*B5Ao z6g}`0SB1^#?&e$k+sxq^fihyT?tfgy{C4Jd|AbO3Rp9=KmG$6?w9yQzV@W9&4xS_% zLGo`^km5Iov1x(%laUuJ{jByn0Z&k{A2MPxcE!B@x?Wqfrm`4)PF_{o$K4NnQjip` z$-%idhJCEHs|<-ro%A<$f$57E6BhBcBnQH4lGN)Xq@^iy&5MQjF<1T4`MeQD&CM(6 z(rHlk_RUqFv~PTYot)R!R+nh4B#?|3wKsd>wdzU!O~%%?-TjD3E8-|Lj-(6ksZjwB zmYWt;MwIjj4fFY?dYH2CN<<$3f(!jIfye2w`#DLU5Tvt zdu|lXr0+WZccrzRh_l3dFmhEDe95A!FMt#vpd4GTrdcS3QdsKKBUYK?_Vq%H2_GB^ zwlLs>PqIWJB`p%)fKL3dwwQ{~CH5&;L`Y^2V7>YUc@k+1R?a#sN3 z_4CcdW`m5$9V4p~v`igt#j)P_;Z!4q*4tf1z>W``X4WljVdrJ4O5c=)39oNl+h7yP zsx6+r5<-)R_)*oCiG;Far1Us1IkD%1&M$mcvYQUDxxJ|$BwNU8ksjf1cSXc-+>H;3 zYwcEr{cCt1F`V1p(N0`I<8qhEF4C6m+9F9?XKzi>qf4~Xc4rNiSqEgzBu0ZMO1`kM zpz`N!7?+Ff-H*;)o2Y>&N{TCN8=wKqk~Q7Vxo6~$Ir2@o+h_M91Z<`W7D+~r|h&_j5oYS;YtaDOe(&Wcow!jVNJILSkWQQe9kQBmb^ zbhH+oldH9xmBY+`-2Z}%w6?!Cd!hs56Spqq9tJev<+u!RwiImdM)SBRRo6~J*|30f z;e)LGN~~?w$0#xvmxnA*oE=VUcDv%1Gjdmi8N*D)149b12ZBTC*7&yeccB)%=#woSZ=q zyl(=sC*8Dt0Br8X-Ci|KvX>RP{L@sw%mSD6+U8DhVvS9b@XZ*O3d;<|mDNfz{Tn8A zhJ_1GsC&_5j)%oD8d(Dca zCbt65#t6=5)A(Aoug3;+!e-%;%Wh$4I`g+sA+2jQSi)jg)QGQc0R2j^0UZ{gHdH5P z#TW*%9v9`uHBej?YjP+Rdg=>y@oQq$-VV~qNrdyD5-gf&1sg(h8b9XS8ClgQ4;`uH zfOrHFOrqur5_LFgCf$!xsFw=}>elV_Jjg^lR*_AD1pvM{F*stN8xl3gEK9anyTLTs zs$d`NrA=;<$nc#0-Urq5x@1Hecy>!X!irAVugDK=sJr~%NAy_nBg_+bX-EFr2)^cJ znRr@{oHN#2XN2Mi8sw}eU(h_`81ZKJFQ~TSav1@pZVsVLU7Ww^l4vHAV*|>7!eHf2 zbFq{NQYX?lH=mklt}?{*T_E^)aMF9$!ywcr+dFAzsV25}G2osGKhzOcKV{>xSYKAJ zYoLX9GxNeJEMpijb2c1jp*c#5q1x~fGu_Vh5gKK-o6!Z^N46x(M`=7IGam(9D#R{7#i^)kUeEi|hi8hy>{{U?@Snn1< z@#3FNc9D{mu_4UP=w{DN3qOR)FKRyWxYp$DC9Nu(0m&v{8Rl+rAx%mMgF5xfE{hr!qQMHq;u@q`v1eIt$m9D2 zRss2W3>*HvFz-w#q^&kok~bi{$=xbnS|@iJpxoPfU13jeFKg|KDMBhTVJeW=BvAfY z7dAr*Dr%+-Uhz{fjRlqqQV$R+f| zZ3~_R_2#A-^B6rRzT_;4rB2^TQ_1epFyLoL_(rIPP-O0!Rq_X(AO6Ycb<*b1kASYp z&`eME!=_1xJZ?VOt%teo{7%P;j@6P>+h^kNylGO7qUEAa(E#s(_2^;F0-==6=sj&r z_dqm`R~HOYmaf(FF`pTRf~X>E1^z&@OEKp|VZXpBZ#2|%J?FLCQFCz*Ir-FNlgy?} zBg59!&_Maz*i*&)$V*u|D)}~3NaXMNZTvfNw!9SUp=nE{Z_a=XCaKDW>BOn>ALxN)w+gW%MsCv zzEWIqt=UqCm$SSCiiMfZ{oMy|qu-93{<2%y)X={j49-h_V2K3O%0;ffWN~<1Q-%xr%(apEki;XqT!gMnfBu=ADbetCf$v77&8ZyIyIn z=6j$m1YEvvCWKeDqaSN(3;s6j%&nJtuXU#m=z&>iD6;SGeS(?JThOa2o8c}|Jg&*w zmi-!C8>Wlr)i%*nKiuc!5kOa2Vt*WHU?-w!1p1kU@3z6`-yKU+DZclwU6W_-fsj?q zu){c2Bbqbwn3VS=0@RjAwWFz#cc$K&u~R4-y3Iu@oj}(EnnC3siQH(oYNcj1{dVKvrm)~whY;JDQFcFy1zLvFL zSRd-NV~EqMyMg2QP&{t!^&6e2nf~2qnyc3$s<}76WuGBx z1iBdnx8r{=+uyaGjpHJ8nC|DfgpI&V-5SI#rl9kA{MEa%xe^QLM;PRG>z^-)B+m~g zucXQiLlz9g3H%=8(&Kf+K`?v)jiX%!*7Ud)z5b&@#%*uKuLa-L4MAYaWRWs|OdpRQ zz7PV(jHXmqeSVD1N&;zxyL;`&X*zu6G9-|T+jCg*;puMT68xS0r(HC@agGY5Pe+j7 z5u~421(CkpQ#z`4T@>hKKiF1b)i72rLuDI&K$`jalQiO1 z_=IfX#r#>j{sHc2OxxzKYD{APVJkNN7AVXFeB%0BZ*S-a)`eehC~SwotPHQzwsq3X z^b)bAGt;B?8aIOFcM|jMeko;mo5HT`e|XzZg>dcO)kG3mIZ%=&*gnHlxX^mun4bg z!+4kDLpXQItX_t;x8eqts;TH_M10PX@EooNDd+lQonZ;X?QgiF?6y!6CwT4F2jqF@ z+{aDPlE9--CaHZ$hGkubcmKM4UK2xRd?=<&u$~cd{Qc8r=d#$%^W=w~Zz*W|_g|ke*E^_QVb|Pn<*s#Q8Z+XR?bVu?s0Iee@-M@A_@!jg9muFRXZaeN8JG zT+=x?auoz@3r>7|i7$QRD{iunPlJBfI+3-XWDG7GiheA#`=lp15m<75Co{4&@_6q0 z@cmujH)PY@mfGYho=Ef=yJEy^C5<>-xU*2o`aSig7X8aZvllO!`B1M5e3L^_=I zTqor=aQ-l*EFcXC?7w)@5Fz>Eq9p9!WaT(x5TvuTb+1o$;Wjv)-xT!OFFTZuF2GI; zCObO`L;%10aSpk8EtS)@-ddELVq2EV-g!MPlN-n*d$vnRNVG-abzHdSShmTn*Qy&) zqa7{Acu1YVSFYvw(!|?5TYE<$;E{Br+G6$#nZ!<};6}5KqYW{uAQ?g??g$Gw$8sZ> zjatJ_{Cm_ZAK z_{B&t$og||G{h*8s3-x3=6Uc4%r|1Q&A9E{ZI-$Wd3Ik2w}(wo!et~T7hV!D`=#v& zj}-mdY!oB~$Ru2RM)d<8H6vnjVM6?JoADyxa2sXrz3nhei?$SVN!aIJ-Bae$=Na>a z$4WMJ#yv9ik{HI+l6(D;ej_CL5j}#Xkwo3NqB zDtSFkpx1|Bd9^(k<3;Or;7YqEZ=B_R;s$AJXkf=&;8x*)AzlW6)lx~Qzn#h z+HZ(%AQ=41@9SIPk6g>1{{HG6efpqQ}VH^9k8Gd;@c3wicI7Wt>Xh*G#J>K^Ur|#k z5wkwQQL88ZKjWomDj)fl+c5}gOYxq{M44{8v-D3LgllL-PrMa~Pao7(y9_D=yL=&W zPS#{kxUj#ZzEr5{3b?Gqs2XWjXCCatE2eBnKZeLDt#?Yy(Jl(=FAi%W9-R(psh0dB zhS$nU13GYR+?pB8P8TAi6vlGQ_(Xp6j~{BkO2&0U&mG?| z%T6yi*#T!ct9TsERmH>uS7y3G`hK@VBWkGg4cmsgEDn2}oOQdg)uKB%8{Kw0Yu~+K z$SG7RmksL*b3>O&a%n?MF*I>u2M33St4h4`VX2AcxCUA1aFITXV^;ABTh8gqJP1=<*;ilzU%@nnbMh;3ea6!OJ${ zzP`Ne_L~qr+YCDcuBQshFVK{EK}SbswFZ4S9J%lng#kP+E-p%{s-eW95qQ|h^^ckZ z@9ADi>Ra2MyYb_MfqF#u57UgM&sXbfO{S0rAULV>rwyCZ!sJG7O~bNnw+ z8v6~uR_pa+Uv_*EQ7PmGLlFg{U>Q1r82cVmgq}SIKJq(X8a#KRk-Nm=Jx^khJ$26E ztVS$FWJ@vwAE6kswW8#4IgK{!^$+qL2fjF(TUx-)adB{%9C!FWuN?zHv8{lo0nw=U z~r{J~RP;b9bXw{>2!BF_*54%!^U=BaU?z$=zYQ8y%O^0nNpEQJ!;u%XJRFbLHaV0wuj( zy9J_S$EU`M`ee6Ks6n>_!S!?&{q*H#J61ZCVt+6g)a!LWa(-l%T)Z8W}%a&vQYqtOru@kH;o%QQX6-q*(qZ1|wvDBZZ)`w(IP(NR@P zi|WKuO+!CS>~e%|fIF?kOc(q@ZeV!paa|=fghCIr6k!Be4eDZrys)HijTIDwLx--@ zpsFH2IZg_)l&tK?2XFkr$2KJP@+($n{gV;Mv&EGQfXzC7YcFpR^r4JhQX5RrZkMN;C+`;M@Z-+z;_ z^Vj;);ZCD<_@N;OAkxP9e>_J+9GhBn88J~k6Z$_Y57A4;wM$>NB5Ah`VKh_YR9WB4 zGR{{rvlY%&EU&EC#4Y2wTWV5e)(1ire^!^oSbCg$$n9fiw~DVt~6Pxz;IrR207^;Vv!<2wd1HvDjqhNkm zf+OS21KxyB!51ETjeYRqH0X)(lApn@J#QR3nX$JOAEG)cex+9mmv~?KF*wBfjGOaH z6AAelLI*lH$9|9oBmnf^m8X@tsKss+h?8Q!^U+;kwafkzuik01y~pF++V zKgwCuj`pd@3unoA66~7^^8UH2D1frx!vtR?SNGo@IDqF#o~IE(G|2f1%8xnfE>Cys zAr1(Z!FQlX`bRSfa%*jg-(%wIO=mz+EWx}eRSUIxEGCujqdBv7iP#1I8RZUA^HO8Y zv~x$}@oRt4$k2XfV{z5AIUJ@3Ev6U`|L%;gp;t`!gIIr+P)BkT!Xz`l^k*|({W^~R ziL0fW`7M5V%U7tX*Jim|!X;mPqxF_ah0YzAdkfrkF+hKgQ;BL*kKCdU?aG9M1GRsx zsgzF^z0XvXf5QhsGNs?)d?CO*tal!KI+Kxbd0~Ntj+}h7z9EY2DchFc!~0?&;Y`Fm z)EhAt#Csb@JemG9&viDE3}3etomRt7j8Z}+Ez^M|J3%{kbU+UUWSw6ye+AK`FcMp< zoVpn9YOBk2gmd8SccQsVnXCfO`M+W|Lw+2lx4O#8`dX-`B391>UB916XXC3^fG`iC zCM!eUO4oGwgDew$R2*9>!FxDVGKM>{*JjiAmL6Y5SP!*t|277eZ zk)8r9xY7&_jm~DD*L7dMR$G}y?bCVd3ka{{xer>$=*JMa4O46+8-V zh<=K>tcJe+7uK8Bqg-0-^G$<0b`i;X#;NyYMVOKXs7|4N38ApB5rga1~-!#v9UT(S^8flyJXX{2d zz=4NlG3C4yAD@6Q&x5uam*2|jxtNF%TJYnB&Cl~j%Z6W^$e0-6q%l*}$l&6{w0Mn> z@C}LmiM~*hR-?+X)XE00+Bs9W~){W zT zw$iR^sED+%^D{udw1|HTB6?)bz!m|m!K74epXl)RpCe>gh6l499`~oP>}?xlo6e$9 z(j-|AUL((mF&i@#MxxG=_P&vq~B0*bPFP|nH zY#c`Od%>kNn(FjZTW&Ssw=S;_A~~YOk#F(~8%3G1h$GC7B<>pj4u1LtO3OUS*(9r> z1Ec+<(K-F#w3xowXzUALWx#vyjS1}m;qb99m&ekkHB^gv;~YU^3xE&+kW~h?|wvNx{eO z2g2xU1&%e?5gTI-N;B(e{4}W=780XN=BQ?aALuTZ%|pnCBtal z-#k@B+Np<9<$m5{$IbIVxq_3!NsV+fW%;nuTR{DD=AB^B_5IDJI}oJ=MfXm>rhigE zeWQBX-(Ox%k*w~C70Y%f`{2R~7~7ymSsBlk=T#Os;H!OVi~OVI3gqkYK5^O{RbX;c zp2h82?q!KaEHLg*&uTG)#ILhup5K_@9;g!wY+8FdSIF4Cf#r0gxz*>}akhQ@eXCS? zo1L`Y%sPFKkr-C6_6Hai0@UkYiO{q+Kd!95Np^9npg|DVXt&;sl{D|})1`Qy@416K z!IBAJ^bJspaWaI!ywN^L$o;q-Aav||%CMrpDL5(6E0^;3?>jp@tOp4o_S|yXg?jOI zVsvaxO7M%6$M)28MaEu6 zRjeo@zx^xp#LMOxm?3g>_q$FDM$|R+$=}zzK;8bH{02d~2|t+i?@#;?@oEJtJe?)D zDL-VUnGZ;t6ZnvGgSih1wJIH-b3VN16@1+TEn>s+4xl6*wj0lHoPpl57cers{Ce5N z%zxR+9;qActJ#vA06MEV&jxei6AAWp&1BH0NjfRe*^ixv{a0+O0Mmg2=&|rcN#0Ss z`{+zK1?J)hCtNh~c1H%}Mvtd6W1A?qgm&LoI>6Ymih#SQ=9NX@@L)MxAYbZsokrk6 zoGjAib2*@&59WEdE@lq5sHgJ3_(CFkI=qA6Td-ajn@#p2ps-L~T|t>i=rH{lTgX7? zUYKkjq#(;dI5DQlMgYx269(SL>0c5x7TsRD+S=KqG-7-QLAsf5DYOR)z63>s;d7GX zWlon)u`YpJ9YmZ!q{C{l9A{ryWSnSgfM zb^nU9`dgH%*gx&Q+WV^;Ud=GaP^Z_6`7~nTqXnDaRk5(bZ^y?6hMN-*0{&AzM(uD6 zr5p>R5y;3*AcdFcQ_Lj6DM5$!AZfKriYkpeVX!7EE1OS|dJdZpzSrr&o#K}gXYKCZ zCeLaiw@aj$%W>_k;>{|Zr9gu1^dx8eoLPK6N#S3{|FLx+HTy{> znJabNa7@NAhoLHAbE9jfFPY5K3I~?_y>>D!62_J-5_3vqJwaZ|?kO{%wO#s#?$7Qc zUAs?(-L=`zub50P_qq&_f%u%B&OaZ|agGZ!{Z? z)Np(5d4x=ijX?C!FN-~WJ5@v=YYIx@B`I2 zBE1yFdW@3}Qg*h_rg@mEC-Q6|KzPLe*j|TO9nUiW=c8;xxf{j`-o(qH1py>h-|bxK zgHv=!t`8j2y!G)rM#GY;G!XX06?IgFgqxSaD&`jlgKq3#iG`RiUfpuF0%K{R?^6g= z9A)lPj!fPZLA_26-|L~glyA{;rMDIpiX8CsX=M)3KgpWRbhK*!)`J?J!ow!9UinBN z#Z|8DVhAZWqiY!aj@#SW#TiVH80f!X8F=(1-O9?cmSiPP0@Nrk#B+o+&No2qPkByy zwKT&%^+VMYrpM(l^ffdE&2~VbyM?!5{n7AYSfws0XQeawdzK?cy~I?-!J6}h+$1E& zz&~v}^w${^+|(M(se3!_3r)MZ_N3=&rjN6W+egX=EdBr{9=Efj~aVEYtZ=*QUw z;@US#N4QBM*MjKlE19~})V-vDem@NnI7trv@#B2`^>&t{RV8%WS4ZGRdo}8X_6gnS zG`->%-|v`#+G@;=vnFm)W{&A#UV-YN=1_dBWQXxs%*;a~VxE^belIVDrtjIx#zN&i zG1RutlM|p5jGm^>YSEhO)H~&T#8Wh$WO4fTu=)yAU#6$d6d?9nE|ZR1 zxYdg4)aac2r5LywMM^Ux5_Xj`%D7!Wok;n3;9{&%!sZx7TOR0Wg)JU-$P(5pee$i= zMkkuL`6r(p-*j5q@potVwoGe=fiXR-&_@J64nC99X?u)P&hSk-I&T5~H4rQux_oKav>Yu9AQpY)COIEhg);T1Dg z>q{e;EX>Ly%TIal!J4KTGX8zpt=km5=IMYv39W4{;dLL3z$d+`EzGpgOSk_!Y{oH@ zaHX!7I8ZsJG^@7Vr+E&+T^Zuubu=H3#52vLWWP0winD;@i=WYFF1-EHTmqZ z@wX#I`b0p=dSyvQrO)+&3>s1rV!l4v=RjqdrTyrpHaRu3;AL5U-RrnzTQS1<#S6mU zN^9ddx0Tc|AXm-^Gaw9&HUqwO`*Zib*Rq+6ysU&SdSybnW#kjv>XklgXBX6HSF)0X zN{dcuWD{rcOAnm&L!~pJ34(mnT+$Z2S5lpsP4nL~vD&gSo)n4nEFCF$@jb0#lquIB zw9ptc!)UU0M9Podg*z)Eb%pyG;+M8f$5rf0C~f(r%-Q%QP=DW|w0PvjNt*&3V$>Ur z+()?o3p@c#MJ1Cd54?Nh%wkC;Rz{)%=&mk(4*|sW+tVz z<0Zuwj!9M`jg!KREkoG@^-(=q!N6FQO8{!`>!om8?5voPe8hSKwK@}!pjVV|T>+5n zyL<~qmdQ;Y_st_Bm*R2i!x71UcAZrSzWchM3DvzvX)^G}EL0Eac?Qs4>gz`$BYDC+ z2ko9dvPG4IJWe?Y)r#s_$+Q}is8Qf!$V`K|_CaNxf1`{qvWr=qjMGY7M6;aCc_Mh- zRV+Vs9s}(7+UwA<#cr&5PcK2v z6dHEgH(g@-^cp4N_ND+V3itLmh$i{CMc@zQ7KHK&RE_A{;!Z+nG5;*_`RQ$=qk7c~ z+Cee5Kshe&BpFG6o3Cf(oeq1bY|1Yw{cNw06kv}CVw@k z6d#O-9kJ@>Cdo|{>gQCeip?8|kt)A2WzkgUFIf>tqmD&a4U>n`7*Ef_g!Z6e?N{b8 zGriQ>BUQ;6G(AY7vMS5iq#PT$%VV~q7}W;oyTu2e_^ct~%CL;lO&i=$hfBZCg`2-7 zXxJzY$>|O@CR+ref zzAMusv_{@hvKYNII0EQtZ6+#QplJkMJcyg~n)~HQnqkd?_aW_LQn(v2Wtg z%0+9dCc)^oMIz0un^h@qwzwu90AW6tx$`%`h{sXq!+jv(sxkK@Scd@YRY`7cQ-O}X zOg*e-f;8sJwmimLrlQ$YJ73T(M{ z%`(yl*zimW3JX34F{|-+P(-nSfpdyJzIZ%GtV7?Mrs9(rJXu(GJ@JYgGCMXl7R0io zd+bS!3j(UivGa zwqFJWw1X_<=zH}_{$tefzaM>oW3k2LW0Wg= zNRzV8)#Bmcckxb(0p=LKom5}&)92FJ0J6LlUg-*V(skVI64D}aGcHtDOP4=R$z`2a z-DnLw#b_6)96E(k096Ps5RkK28h;{-#-yCVjO}`X4`Y@O&J&|N_QcTlvwU(DkUjrN z89sB9k3a88sa)DI6wh#Vzn-HUO{A)MM68d%rBo~Q=2&0L{PV|#{;oFJuJY^bv=ru7 zRLBpn)@Z$7SRw|(3o1Yb-rVcZOs0^$D|h2#Yarx-9)?FGkZHzD&ExaejPc7N{$+C8 z5m3ZtB0-L!^u{hPb;8mH7F-As=1Pkany_v9wSFQLyPZI^ZAtEK5(66@YRnH>n2jPL zY=M+rWxQ4gBjVOSYAP(>0v~VB;f&Z&<3yJ~@E}BmM6&1dC6KqImFQeY4+0I_z3OVo z^D56$tICy?EKZ~~nIyJ&KDF-chv{pP+F~*t9&24hcXE5GV z)6#gc-%Qw~rWRjNEDMC9`u1}q(#gl4J|!KAxguJLiFnC0L&0Fgzq13M)Qa~=W>}Wn z+={{JAmT5g=}M_aaS#pF;1#+O$^g5xi-gk{KqHT*9=^t-)MP(5oNLQ0avTS}d9N;^BZYV_WJw9QXK5|E?`}A2#d5-5KrS)=#)a=%N+c zo@)B#ut71?WSkfikK<`g(j?aW=o~%{fF?3rI4C8_2rNRytsVSDj(CBI+ISMY0r~ws z6W1FD^nH5)lXmnL?uXAxLfnFFxwMRg5`PEpatJla$-k_d49l>r}hC*;@*MV5iBOYSk20M+W=v3qwQXwmHWgn%2 zfq0sPZ`lZQVVuq0012F6^TIY>&)V3ELR*GpWKP4@NXCka0tc%zxtuOrb*sR!$V8`C zjCh)f1@4@bHok>IovETx`~FO4<*?wL3@h8c6NX;1bl)izv@c*1EB)4tAXjBO%1YcD_| z3x!j`v#o$UPE*7*!{?uv7SZ}3Yur}xtIh^G&mLi3)#YwI#1df$_+1w*Ol&Bvu&vG!8FdTH>|ieeXUvW zD~h&kg1;@D7mt_&MwCEVj@b){UQ}hjfD5EZflz}TFT{0<>PUegIED2`3?4I>aWVm) zu`lLea`BH`hQL>Zt+Ajhi$@zr@fG8MaQa+mUQK^=I%97_%R+3^<@WSmP1PWaHILp~ zi?UV#OEV1Yo;r*16pdu2H*DGw@?Ccgb6YLgcYaTDdNuwN>7{|5e>`(RpfNDMx2)3;&l-R$kHxQnvD)ilSimh#}4|0ib)nDl=+9BHFb z7Z{5nQ}0Gy)i!cb!szt2!sV0+`CVjxQjn6GhI8gzL~>S(h`$lufOT+)uo3JqnbBU7 z2hG!o#?3pi@AajmX~L-5wHNQNT!B%S&cV=;!?9To;-^bq$MCXYSoP#{IMmsSp+CF? z&;8{A6iuChZ{6{G6rOr2JhdL!D%!B?mH%SdsvS^AJdRpigpWR6jqx)shuxWq-4#S% zzXy_kH(qD8#v6*-7MJLfKBE1i2$4SkOpkXG&#c^mNf96XT_HHl zGMdSO%T>)d816!9K`y$QTVa;T+GnL8!>_ zl-85#o)N;jh)OMAh@uP+b|3U1FE$w;-KEH8kMHg>|1ltY@b3!_V{a5=2F z@t$Hdv{_*>97&20TPZ@!IHDb4Pq#5C4@DJQN#rf00K&)NDls-6< zO(%oY@NyWfACRRXZ(QSeg>KIJo(5aFMy^M9lo}EH@t%X(a8LAoGmp{^TCr?>78ycq z@RprpHu9v5(hKJu-qzwblmGZcW?wOB{dKnj0Z&2h-{ z@8{yxmDy|ZZa{qV87_l zL4zH6OeC}5L02-C78HtbH?-b_tyRgm{H!{hazY#2PQ~aBkYb}I%*IF`4!l0znuh-_ z&BN})6i`_Q7rXaTzkOyu*c0VXlmY>id)iUl=|yHR00)2fB$5wS)H$7~AY*pOos1wg zrOlY0Bor};)FuZ>%f+ZB7tfu7$>WMiVZ z`--AKF}uZ#`nnFxe{|Vgx6`%g>Pt`lVGNPeW{$(JZ@d7Xkc-{Y+zE@*im%*sLY!Vz z$l(kIaq$H+pvW#XG&CoyKPvNF4SNqYVD{WUA<))JRxmqpRKQoEu-W&s7}sIr=BMDK z#Wcy!Y;kfN5=P%mE=Q4DA?6YoaOE>WmFjB zlA=QqzCwL%5@G}Gk7f|HsDdXetw~=oig2eA=Ulb^4G?jaBxYc$ud1V{s2<+Vz*mnV z+`xed>E0UnDe|(~dOIYt&mSSLRK{&LUx?b)4meX?`e;w#R+wQjo5UDHZWTr0q25ZI z+aZG+f^|bUOm4n0FA(>Ohv-<09^!#Bvbp=aL7pl>(etU%UVI*`!}mppSM!7xYW!zW zJg_E0E`FyEL-I4RZT0?MUWMG*6%f-H{Pwm>zBXpPuD%Uf8HrQO%KQywbl_QZWP%E`!&fL>*^%ZBw9=`4OPhcV}^T*?Y^C#hikwf(n+w}g$OV(l0zZb)vmJTz; zJHNi|VvHVD+-s&nllU@^sBSS2%=?Y9(wtLIL}QHyS6y>A>2WU<#}Gn9zvq!BKEU07 z{x5j#$AA3uGz9#9{PKat2<=^q%dajItaJ8-cj42etAWfkB&Q`~^@`QBJ%zfOTk+68 zek9C;$J2$YZg~=oJ9hy|WaGRcod1m(0+HBA{$uUvi6X=0`cg()x@d8J@D zgg^h|CBfmR%((zR|H(YOw|o~u&7>$Y#^Dz~nS;ek*Ao&O0g4tEK6ouU_AbPOPy7_$ z{N9i7yWf5{_MQh8E+HiLW6a!{7(06+A!9?Q@yYmL++@>{R9>{?*-0}=eo;U*gD(XewL{rYoJo|8rt@eoV;2Q4=P%JXEUCks#C?y|wt?!iBw{Zu67F1ctL zrc53|;jDrct9i92Kh7AQC!U!%cPfUAECs3?@YwS!apI&KuxiZ?o!W+B`^Q6X5EZLX zK5Z;6ICBzO>YD{pZ9e6ABci`Bzl`Vdw8Lq3;P>~wjFx@XNGi_5-M4>7T>oT!1rBYh zCNes3-aLH?gP}#)Soh&yan5O@$Ofp`yuTf%%=|4@e7seRSgkl%k9!{e0Fpfo_x|4{ za64?UiM&w=^(|e!xZ-mmq8ZvOKab*kITy!g2*3Pg5TV7LF~SFKi(!3;FQV3k&}^z*WO%% zvoHQ5KG`VN{>7Bj#}aLWBHcJ;VkufZUOfH`vFC_f&3mwTis&j658! z?Hn{~$wy}_l93FG9$Oos@ds7jX3|)yQ*rOzSEI}4!}c8qaq_8CaM^`3anrT4V|BGRH{sbp9fl1r z6vou%b_|>(H{9`r*5=U{Y9MVwftdcQ#vMnC8n>?)TjU4}I@AX=ZEL-z9qQ;l*Qxfd zQ;B|OuRi1otH#+or)ule9*_kn~Yfn^kF<-!AzYb#lDm53qF}# zTEaF2rKtwt2D;7N@s-4b`y$0i2Bj04DZ4i!Rw;nY91@=IE<|z%Nt696x>BY1h$IZ8 zPDHw;4gsHLOF(49y3JVhVkU-^&Jn$~k}7?%Q>K3|?owf{sm9AMzk>_To|G^*y!7PD zE45t*8r;|>6#hxnl6Fy>63x=JymVR7rb6+8T2Xc}wXLXiRaDe1H#N)~<||!BO_Oa& zAr-ZC-8M5R%`tDhteQklX*sDas7->Y2qvgV+?LsYVg0!0e0a|Ja^`vFIcMhN2L~Nq zYieT(09YG%C=jAQ}X8vs;m;6eZI)Bm0k_9bqUSo4zUjC4kP$Yz>(?&azG z^TAEEJDVVr>4(lYKJsX0*UYACeCBzEw+DWBcp>EULg?z%H%!ykwb`57nxpyyn06l) zoKmCsTZLD{6)cY(I+L~|i%ZHo;V1Tc2}m<4uh~N9GAfn&*lg>qIVBo@!Q|0&6B?7! zHUjIK*|j%;$ZCAOrV6j=iF#*j6Vv%XSCwzr98fCNv9G{6#ssEkl8a4uJT24-JzF#&A|SORYV+7=ADyu3ZYRWJb>6Ts-wrY3b0GN@yX%+7`l4_LzMgHs(`^`a7$Fp+%)`B8^HIV+d@HavYgH;~$?cu7cT|>Wgps(g;lHYu_<2M-CtbxRd1_)hkHEzExn5pBU{0ghl*djW;vOxA8 zO?7vb_&PihJBaWtDO2w--1`EkaY(9jedBakSu@^QBH7p5a@DeW^Z39XKBd0WjMr|C zRZtmq-4Zmr{j5FW*zQgpo%Ls~P$;DL;IZI$@XyJEOH9*V48OtN1-qLUBi)vMzw7JpB_^R+&YQJ8M67r$ z&BXUP=&~YgskLx(!ow8C@a9dHFx z+8H*o*y)^==wZu6TX^K7w13prj&PK{pVG8Cu~aI4FG7RY?1$5VaN65X{ znE02bexP(SZ7>QMwbCsQ6(&o;;R{isj^|LunRahi8_gPuVvNYrYSbP10t~1NM^0cV zQiZ@T->(k2m4(NJRVWGs27aC&hPi}QX2M}AsDz1dg29tp7I`81;ftlmG!2&Mo4YJ% zvpq*6F;0frXf?FU8B%qMF<6s}j0ixKnU@#%N#~Amw=6l4vxm+(}=y%t^lxa(C@5Q$TiOLBqBC+0@5Lr6rVm%^P|V& zD(@_e`y6aa;kD56F;=wF8@B!^vwUx>+=k_!#!IvRVR;*~aE_A~Y#n+w9N(njTcjCR zZ_-&7hTBgsL(uAgkNEN!OZG{jp`jNP8fEfsWW=F9g2C3wQ4?+od3DXr$qR3aOvx4H z<>eaSAuXIGt(Hg(93C19ij9xIk4B?6drOapi*LT`c)Y#6UDwhwIu!nO65~BemY(Z2 zE9#ul<`Z{yaf+EG0pW$l?Wf({Wj0J27G@$Mng!CUJjlYCZYoxqIp6=lq%pj8WfFlv z2%Kp7<^W@ZOJ+r{FTIQOQ{9$Gnpaj!Y{*V7 z)|sGyVPb#J{DH52K!Qe2#F^J%7mwSu`8NYW_57?6dsXk%PW@q*)J&LZAPTiXmN0U1 zaxf^=SZqdygwU0g^a3wmNceFT<*V!V-3OC^0C|zan5YJV_6^7>T4P+)UJ6!zZx_h- zel9N_n74T4xWc<*cq;6agG1Z;fRV9r{|cAoaadef+cgXN2lO!30!m0DZ>PO0q{KN# zql_y4X7k!JeTeTEgg>mSewksfbFS^^bJq;wlOI;Yt}c!I>yb7Ey)7V&H(@b~-$gF% zItPdIWWV;B`pvezpo_^`0GOyRha&iPCqL7a*@yZEmXmR%4-W!kZvt zI7;Jt?zS2%l8~Ugn~``47v7qpK_$0g(yn@vc(rtfw9R>fmtqf`)A*Q`%Te_p01%H-j{(pp6v8GX>jIwK@zTmWg})nMFQF zZ@SzoY3oj2WY-{TA3uIf`oviu)~4JlEiJW^-LZoGLZMKWU*)QURZhu#aXg*nLF>7E z&`B4mSCEcrTOqU(QdF(asBOF@CC}V!PnzrF794gFe{5)IDB290)=yho2-54{26F{l z@fA{G#UQ=7*JMN9Y)+oj&hDdmM(m^A8dALZ%#f;OBJ;W3!DBL+0}rHF7FqH~XAX$B zv)$jF?<1Uvj9e_|_5}iHyw1Y%a$E8r9Tc|@LM(wGFy!iXT@mqr-z(V=us(B+9X&6l zuzSJl?n<8J@Q2qnP?c>vTE?ym?-g5sonX2>!=vg`e%?kNwO8Jws2pR=oJI=@~B>6zUxHUaJy^1?WQrsFkL)F~5j>qGjRd5an8S|-%{gz60_aTb? zYK@MnZhMAe6h4hLM#}46zRWn5xii~E)#?c_mwP&kv}!*BB1OV|x%y*4XeGtoa&ei5 zxUi1Rbfb+T?mK2(;r)7NBIRhAZ;T9Xr6lAz(OJupC~JT@5_cqV?~F0o74do6ev42o z*DAG<Y?)kA$2$m>nKVEfxw~044P-cq0ot+ zJ;Wyzp_;97P%(|N(I3~Uh|1Y!n=VlUtk=@;YzRPz8}whBDupW+K)}!E{MiQYs2l$V DDdeH< literal 0 HcmV?d00001 diff --git a/lib/integration/client.inc.php b/lib/integration/client.inc.php index e9fb1a0..6855286 100644 --- a/lib/integration/client.inc.php +++ b/lib/integration/client.inc.php @@ -8,33 +8,31 @@ class paymill_client{ public function __construct($client_email,$client_desc){ global $wpdb; + load_paymill(); // this function-call can and should be used whenever working with Paymill API - require_once(PAYMILL_DIR.'lib/api/Clients.php'); - - // create client object - $clientsObject = new Services_Paymill_Clients( - $GLOBALS['paymill_settings']->paymill_general_settings['api_key_private'], - $GLOBALS['paymill_settings']->paymill_general_settings['api_endpoint'] - ); - // get client cache - $query = 'SELECT * FROM '.$wpdb->prefix.'paymill_clients WHERE paymill_client_email="'.$client_email.'"'; - $client_cache = $wpdb->get_results($query,ARRAY_A); - $client = false; + $sql = $wpdb->prepare('SELECT * FROM '.$wpdb->prefix.'paymill_clients WHERE paymill_client_email=%s', + array( + $client_email + )); + + $client_cache = $wpdb->get_results($sql,ARRAY_A); if(count($client_cache) > 0 && ($client_cache[0]['paymill_client_email'] != $client_email || $client_cache[0]['paymill_client_description'] != $client_desc)){ // update client in paymill - $params = array( - 'id' => $client_cache[0]['paymill_client_id'], - 'email' => $client_email, - 'description' => $client_desc, - 'source' => serialize($GLOBALS['paymill_source']) - ); - $client = $clientsObject->update($params); + $GLOBALS['paymill_loader']->request_client->setId($client_cache[0]['paymill_client_id']); + $GLOBALS['paymill_loader']->request_client->setEmail($client_email); + $GLOBALS['paymill_loader']->request_client->setDescription($client_desc); + + // @todo: handle response + $client = $GLOBALS['paymill_loader']->request->update($GLOBALS['paymill_loader']->request_client); // update local cache - $query = 'UPDATE '.$wpdb->prefix.'paymill_clients SET paymill_client_description="'.$client_desc.'" WHERE paymill_client_email="'.$client_email.'"'; - $wpdb->query($query); + $wpdb->query($wpdb->prepare('UPDATE '.$wpdb->prefix.'paymill_clients SET paymill_client_description=%s WHERE paymill_client_email=%s', + array( + $client_desc, + $client_email + ))); do_action('paymill_paybutton_client_updated', array( 'client' => $client, @@ -44,16 +42,15 @@ public function __construct($client_email,$client_desc){ // try loading the client }elseif(count($client_cache) > 0){ - $client = $clientsObject->getOne($client_cache[0]['paymill_client_id']); - if($client['http_status_code'] == 404){ - $client = false; - } - } - if($client == false){ - $client = $clientsObject->create(array( - 'email' => $client_email, - 'description' => $client_desc - )); + $GLOBALS['paymill_loader']->request_client->setId($client_cache[0]['paymill_client_id']); + $client = $GLOBALS['paymill_loader']->request->getOne($GLOBALS['paymill_loader']->request_client); + // client does not exist in Paymill, so create + }else{ + $GLOBALS['paymill_loader']->request_client->setEmail($client_email); + $GLOBALS['paymill_loader']->request_client->setDescription($client_desc); + + // @todo: handle response + $client = $GLOBALS['paymill_loader']->request->create($GLOBALS['paymill_loader']->request_client); // insert new client in local cache if(get_current_user_id()){ @@ -61,9 +58,16 @@ public function __construct($client_email,$client_desc){ }else{ $user_id = 0; } - - $query = 'INSERT INTO '.$wpdb->prefix.'paymill_clients (paymill_client_id, paymill_client_email, paymill_client_description, wp_member_id) VALUES ("'.$client['id'].'", "'.$client_email.'", "'.$client_desc.'", "'.$user_id.'")'; - $wpdb->query($query); + + $wpdb->query($wpdb->prepare('INSERT INTO '.$wpdb->prefix.'paymill_clients + (paymill_client_id, paymill_client_email, paymill_client_description, wp_member_id) + VALUES (%s,%s,%s,%s)', + array( + $client->getId(), + $client_email, + $client_desc, + $user_id + ))); do_action('paymill_paybutton_client_created', array( 'client' => $client, @@ -71,7 +75,6 @@ public function __construct($client_email,$client_desc){ 'client_desc' => $client_desc )); } - $this->client = $client; } diff --git a/lib/integration/magicmembers.inc.php b/lib/integration/magicmembers.inc.php index 3d57a12..188d1db 100644 --- a/lib/integration/magicmembers.inc.php +++ b/lib/integration/magicmembers.inc.php @@ -4,6 +4,18 @@ * Paymill payment module */ + +if(!function_exists('paymill_mgm_errorHandling')){ + function paymill_mgm_errorHandling($errors){ + global $woocommerce; + + foreach($errors as $error){ + $output .= '